Skip to content

Commit 7f9cf36

Browse files
skaiaaAnna Przybycieńadehadametzger
authored andcommitted
Create token auth option (pycontribs#991)
* Added quick token_auth to client * Create TokenAuth class and properly implement token authorization * add test for bearer token auth Co-authored-by: Anna Przybycień <[email protected]> Co-authored-by: adehad <[email protected]> Co-authored-by: Alex Metzger <[email protected]>
1 parent 74790c2 commit 7f9cf36

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

jira/client.py

+37-4
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,19 @@ def start_session(self):
264264
self._get_session(self.__auth)
265265

266266

267+
class TokenAuth(AuthBase):
268+
"""Bearer Token Authentication"""
269+
270+
def __init__(self, token: str):
271+
# setup any auth-related data here
272+
self._token = token
273+
274+
def __call__(self, r: requests.PreparedRequest):
275+
# modify and return the request
276+
r.headers["authorization"] = f"Bearer {self._token}"
277+
return r
278+
279+
267280
class JIRA:
268281
"""User interface to Jira.
269282
@@ -325,7 +338,8 @@ def __init__(
325338
self,
326339
server: str = None,
327340
options: Dict[str, Union[str, bool, Any]] = None,
328-
basic_auth: Union[None, Tuple[str, str]] = None,
341+
basic_auth: Optional[Tuple[str, str]] = None,
342+
token_auth: Optional[str] = None,
329343
oauth: Dict[str, Any] = None,
330344
jwt: Dict[str, Any] = None,
331345
kerberos=False,
@@ -347,8 +361,8 @@ def __init__(
347361
or ``atlas-run-standalone`` commands. By default, this instance runs at
348362
``http://localhost:2990/jira``. The ``options`` argument can be used to set the Jira instance to use.
349363
350-
Authentication is handled with the ``basic_auth`` argument. If authentication is supplied (and is
351-
accepted by Jira), the client will remember it for subsequent requests.
364+
Authentication is handled with the ``basic_auth`` or ``token_auth`` argument.
365+
If authentication is supplied (and is accepted by Jira), the client will remember it for subsequent requests.
352366
353367
For quick command line access to a server, see the ``jirashell`` script included with this distribution.
354368
@@ -369,8 +383,11 @@ def __init__(
369383
* check_update -- Check whether using the newest python-jira library version.
370384
* headers -- a dict to update the default headers the session uses for all API requests.
371385
372-
basic_auth (Union[None, Tuple[str, str]]): A tuple of username and password to use when
386+
basic_auth (Optional[Tuple[str, str]]): A tuple of username and password to use when
373387
establishing a session via HTTP BASIC authentication.
388+
389+
token_auth (Optional[str]): A string containing the token necessary for (PAT) bearer token authorization.
390+
374391
oauth (Optional[Any]): A dict of properties for OAuth authentication. The following properties are required:
375392
376393
* access_token -- OAuth access token for the user
@@ -466,6 +483,8 @@ def __init__(
466483
self._session.headers.update(self._options["headers"])
467484
elif jwt:
468485
self._create_jwt_session(jwt, timeout)
486+
elif token_auth:
487+
self._create_token_session(token_auth, timeout)
469488
elif kerberos:
470489
self._create_kerberos_session(timeout, kerberos_options=kerberos_options)
471490
elif auth:
@@ -3412,6 +3431,20 @@ def _create_jwt_session(
34123431
self._session.verify = bool(self._options["verify"])
34133432
self._session.auth = jwt_auth
34143433

3434+
def _create_token_session(
3435+
self,
3436+
token_auth: str,
3437+
timeout: Optional[Union[Union[float, int], Tuple[float, float]]],
3438+
):
3439+
"""
3440+
Creates token-based session.
3441+
Header structure: "authorization": "Bearer <token_auth>"
3442+
"""
3443+
verify = self._options["verify"]
3444+
self._session = ResilientSession(timeout=timeout)
3445+
self._session.verify = verify
3446+
self._session.auth = TokenAuth(token_auth)
3447+
34153448
def _set_avatar(self, params, url, avatar):
34163449
data = {"id": avatar}
34173450
return self._session.put(url, params=params, data=json.dumps(data))

tests/test_client.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ def prep():
1616

1717

1818
@pytest.fixture(scope="module")
19-
def test_manager():
19+
def test_manager() -> JiraTestManager:
2020
return JiraTestManager()
2121

2222

2323
@pytest.fixture()
24-
def cl_admin(test_manager):
24+
def cl_admin(test_manager: JiraTestManager) -> jira.client.JIRA:
2525
return test_manager.jira_admin
2626

2727

2828
@pytest.fixture()
29-
def cl_normal(test_manager):
29+
def cl_normal(test_manager: JiraTestManager) -> jira.client.JIRA:
3030
return test_manager.jira_normal
3131

3232

@@ -194,3 +194,24 @@ def test_headers_unclobbered_update_with_no_provided_headers(no_fields):
194194

195195
# THEN: we have not affected the other headers' defaults
196196
assert session_headers[invariant_header_name] == invariant_header_value
197+
198+
199+
def test_token_auth(cl_admin: jira.client.JIRA):
200+
"""Tests the Personal Access Token authentication works."""
201+
# GIVEN: We have a PAT token created by a user.
202+
pat_token_request = {
203+
"name": "my_new_token",
204+
"expirationDuration": 1,
205+
}
206+
base_url = cl_admin.server_url
207+
pat_token_response = cl_admin._session.post(
208+
f"{base_url}/rest/pat/latest/tokens", json=pat_token_request
209+
).json()
210+
new_token = pat_token_response["rawToken"]
211+
212+
# WHEN: A new client is authenticated with this token
213+
new_jira_client = jira.client.JIRA(token_auth=new_token)
214+
215+
# THEN: The reported authenticated user of the token
216+
# matches the original token creator user.
217+
assert cl_admin.myself() == new_jira_client.myself()

0 commit comments

Comments
 (0)