◐ Shell
reader mode source ↗
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
28 changes: 23 additions & 5 deletions docs/api-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,32 @@ Note on password authentication

GitLab has long removed password-based basic authentication. You can currently still use the
`resource owner password credentials <https://docs.gitlab.com/ee/api/oauth2.html#resource-owner-password-credentials-flow>`_
flow to obtain an OAuth token.

However, we do not recommend this as it will not work with 2FA enabled, and GitLab is removing
ROPC-based flows without client IDs in a future release. We recommend you obtain tokens for
automated workflows as linked above or obtain a session cookie from your browser.

For a python example of password authentication using the ROPC-based OAuth2
flow, see `this Ansible snippet <https://github.com/ansible-collections/community.general/blob/1c06e237c8100ac30d3941d5a3869a4428ba2974/plugins/module_utils/gitlab.py#L86-L92>`_.

Managers
========
Expand Down
3 changes: 1 addition & 2 deletions docs/cli-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ We recommend that you use `Credential helpers`_ to securely store your tokens.
<https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html>`__
to learn how to obtain a token.
* - ``oauth_token``
- An Oauth token for authentication. The Gitlab server must be configured
to support this authentication method.
* - ``job_token``
- Your job token. See `the official documentation
<https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts>`__
Expand Down
68 changes: 37 additions & 31 deletions gitlab/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import gitlab.config
import gitlab.const
import gitlab.exceptions
from gitlab import _backends, utils

REDIRECT_MSG = (
"python-gitlab detected a {status_code} ({reason!r}) redirection. You must update "
Expand Down Expand Up @@ -41,8 +41,6 @@ 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
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
Expand All @@ -51,6 +49,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

Keyword Args:
requests.Session session: HTTP Requests Session
Expand All @@ -64,8 +63,6 @@ def __init__(
oauth_token: Optional[str] = None,
job_token: Optional[str] = None,
ssl_verify: Union[bool, str] = True,
http_username: Optional[str] = None,
http_password: Optional[str] = None,
timeout: Optional[float] = None,
api_version: str = "4",
per_page: Optional[int] = None,
Expand All @@ -74,6 +71,8 @@ def __init__(
user_agent: str = gitlab.const.USER_AGENT,
retry_transient_errors: bool = False,
keep_base_url: bool = False,
**kwargs: Any,
) -> None:
self._api_version = str(api_version)
Expand All @@ -92,11 +91,9 @@ def __init__(
self.ssl_verify = ssl_verify

self.private_token = private_token
self.http_username = http_username
self.http_password = http_password
self.oauth_token = oauth_token
self.job_token = job_token
self._set_auth_info()

#: Create a session object for requests
_backend: Type[_backends.DefaultBackend] = kwargs.pop(
Expand All @@ -105,6 +102,7 @@ def __init__(
self._backend = _backend(**kwargs)
self.session = self._backend.client

self.per_page = per_page
self.pagination = pagination
self.order_by = order_by
Expand Down Expand Up @@ -271,8 +269,6 @@ def from_config(
job_token=config.job_token,
ssl_verify=config.ssl_verify,
timeout=config.timeout,
http_username=config.http_username,
http_password=config.http_password,
api_version=config.api_version,
per_page=config.per_page,
pagination=config.pagination,
Expand Down Expand Up @@ -471,41 +467,51 @@ def set_license(self, license: str, **kwargs: Any) -> Dict[str, Any]:
return result

def _set_auth_info(self) -> None:
tokens = [
token
for token in [self.private_token, self.oauth_token, self.job_token]
if token
]
if len(tokens) > 1:
raise ValueError(
"Only one of private_token, oauth_token or job_token should "
"be defined"
)
if (self.http_username and not self.http_password) or (
not self.http_username and self.http_password
):
raise ValueError("Both http_username and http_password should be defined")
if tokens and self.http_username:
raise ValueError(
"Only one of token authentications or http "
"authentication should be defined"
)

self._auth: Optional[requests.auth.AuthBase] = None
if self.private_token:
self._auth = _backends.PrivateTokenAuth(self.private_token)

if self.oauth_token:
self._auth = _backends.OAuthTokenAuth(self.oauth_token)

if self.job_token:
self._auth = _backends.JobTokenAuth(self.job_token)

if self.http_username and self.http_password:
self._auth = requests.auth.HTTPBasicAuth(
self.http_username, self.http_password
)

@staticmethod
def enable_debug() -> None:
import logging
Expand Down
33 changes: 33 additions & 0 deletions gitlab/oauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
8 changes: 8 additions & 0 deletions tests/functional/api/test_gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import requests

import gitlab


@pytest.fixture(
Expand All @@ -22,6 +23,13 @@ def test_auth_from_config(gl, gitlab_config, temp_dir):
assert isinstance(test_gitlab.user, gitlab.v4.objects.CurrentUser)


def test_no_custom_session(gl, temp_dir):
"""Test no custom session"""
custom_session = requests.Session()
Expand Down
40 changes: 30 additions & 10 deletions tests/unit/test_gitlab_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@
from gitlab import Gitlab
from gitlab._backends import JobTokenAuth, OAuthTokenAuth, PrivateTokenAuth
from gitlab.config import GitlabConfigParser


@pytest.fixture
Expand Up @@ -91,24 +118,17 @@ def test_job_token_auth():
assert "Authorization" not in p.headers


def test_http_auth():
gl = Gitlab(
"http://localhost",
http_username="foo",
http_password="bar",
api_version="4",
)
p = PreparedRequest()
p.prepare(url=gl.url, auth=gl._auth)
assert gl.private_token is None
assert gl.oauth_token is None
assert gl.job_token is None
assert isinstance(gl._auth, requests.auth.HTTPBasicAuth)
assert gl._auth.username == "foo"
assert gl._auth.password == "bar"
assert p.headers["Authorization"] == "Basic Zm9vOmJhcg=="
assert "PRIVATE-TOKEN" not in p.headers
assert "JOB-TOKEN" not in p.headers


@responses.activate
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/test_oauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Toggle all file notes Toggle all file annotations