summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/client.py48
-rw-r--r--gitlab/oauth.py33
2 files changed, 72 insertions, 9 deletions
diff --git a/gitlab/client.py b/gitlab/client.py
index c3982f3..0196a8f 100644
--- a/gitlab/client.py
+++ b/gitlab/client.py
@@ -12,7 +12,7 @@ import gitlab
import gitlab.config
import gitlab.const
import gitlab.exceptions
-from gitlab import _backends, utils
+from gitlab import _backends, oauth, utils
REDIRECT_MSG = (
"python-gitlab detected a {status_code} ({reason!r}) redirection. You must update "
@@ -41,8 +41,8 @@ class Gitlab:
the value is a string, it is the path to a CA file used for
certificate validation.
timeout: Timeout to use for requests to the GitLab server.
- http_username: Username for HTTP authentication
- http_password: Password for HTTP authentication
+ http_username: Username for OAuth ROPC flow (deprecated, use oauth_credentials)
+ http_password: Password for OAuth ROPC flow (deprecated, use oauth_credentials)
api_version: Gitlab API version to use (support for 4 only)
pagination: Can be set to 'keyset' to use keyset pagination
order_by: Set order_by globally
@@ -51,6 +51,7 @@ class Gitlab:
or 52x responses. Defaults to False.
keep_base_url: keep user-provided base URL for pagination if it
differs from response headers
+ oauth_credentials: Password credentials for authenticating via OAuth ROPC flow
Keyword Args:
requests.Session session: HTTP Requests Session
@@ -74,6 +75,8 @@ class Gitlab:
user_agent: str = gitlab.const.USER_AGENT,
retry_transient_errors: bool = False,
keep_base_url: bool = False,
+ *,
+ oauth_credentials: Optional[oauth.PasswordCredentials] = None,
**kwargs: Any,
) -> None:
self._api_version = str(api_version)
@@ -96,7 +99,7 @@ class Gitlab:
self.http_password = http_password
self.oauth_token = oauth_token
self.job_token = job_token
- self._set_auth_info()
+ self.oauth_credentials = oauth_credentials
#: Create a session object for requests
_backend: Type[_backends.DefaultBackend] = kwargs.pop(
@@ -105,6 +108,7 @@ class Gitlab:
self._backend = _backend(**kwargs)
self.session = self._backend.client
+ self._set_auth_info()
self.per_page = per_page
self.pagination = pagination
self.order_by = order_by
@@ -522,22 +526,48 @@ class Gitlab:
self.headers.pop("Authorization", None)
self.headers["PRIVATE-TOKEN"] = self.private_token
self.headers.pop("JOB-TOKEN", None)
+ return
+
+ if not self.oauth_credentials and (self.http_username and self.http_password):
+ utils.warn(
+ "Passing http_username and http_password is deprecated and will be "
+ "removed in a future version.\nPlease use the OAuth ROPC flow with"
+ "(gitlab.oauth.PasswordCredentials) if you need password-based"
+ "authentication. See https://docs.gitlab.com/ee/api/oauth2.html"
+ "#resource-owner-password-credentials-flow for more details.",
+ category=DeprecationWarning,
+ )
+ self.oauth_credentials = oauth.PasswordCredentials(
+ self.http_username, self.http_password
+ )
+
+ if self.oauth_credentials:
+ post_data = {
+ "grant_type": self.oauth_credentials.grant_type,
+ "scope": self.oauth_credentials.scope,
+ "username": self.oauth_credentials.username,
+ "password": self.oauth_credentials.password,
+ }
+ response = self.http_post(
+ f"{self._base_url}/oauth/token", post_data=post_data
+ )
+ if isinstance(response, dict):
+ self.oauth_token = response["access_token"]
+ else:
+ self.oauth_token = response.json()["access_token"]
+ self._http_auth = self.oauth_credentials.basic_auth
if self.oauth_token:
self.headers["Authorization"] = f"Bearer {self.oauth_token}"
self.headers.pop("PRIVATE-TOKEN", None)
self.headers.pop("JOB-TOKEN", None)
+ return
if self.job_token:
self.headers.pop("Authorization", None)
self.headers.pop("PRIVATE-TOKEN", None)
self.headers["JOB-TOKEN"] = self.job_token
- if self.http_username:
- self._http_auth = requests.auth.HTTPBasicAuth(
- self.http_username, self.http_password
- )
-
@staticmethod
def enable_debug() -> None:
import logging
diff --git a/gitlab/oauth.py b/gitlab/oauth.py
new file mode 100644
index 0000000..f4fecf8
--- /dev/null
+++ b/gitlab/oauth.py
@@ -0,0 +1,33 @@
+import dataclasses
+from typing import Optional
+
+
+@dataclasses.dataclass
+class PasswordCredentials:
+ """
+ Resource owner password credentials modelled according to
+ https://docs.gitlab.com/ee/api/oauth2.html#resource-owner-password-credentials-flow
+ https://datatracker.ietf.org/doc/html/rfc6749#section-4-3.
+
+ If the GitLab server has disabled the ROPC flow without client credentials,
+ client_id and client_secret must be provided.
+ """
+
+ username: str
+ password: str
+ grant_type: str = "password"
+ scope: str = "api"
+ client_id: Optional[str] = None
+ client_secret: Optional[str] = None
+
+ def __post_init__(self) -> None:
+ basic_auth = (self.client_id, self.client_secret)
+
+ if not any(basic_auth):
+ self.basic_auth = None
+ return
+
+ if not all(basic_auth):
+ raise TypeError("Both client_id and client_secret must be defined")
+
+ self.basic_auth = basic_auth