Skip to content

Commit

Permalink
Create token auth option (#991)
Browse files Browse the repository at this point in the history
* 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ń <aprzybycien@atlassian.com>
Co-authored-by: adehad <26027314+adehad@users.noreply.github.com>
Co-authored-by: Alex Metzger <asm@asm.io>
  • Loading branch information
4 people authored Oct 24, 2021
1 parent e68bf79 commit 73d056e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
41 changes: 37 additions & 4 deletions jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,19 @@ def start_session(self):
self._get_session(self.__auth)


class TokenAuth(AuthBase):
"""Bearer Token Authentication"""

def __init__(self, token: str):
# setup any auth-related data here
self._token = token

def __call__(self, r: requests.PreparedRequest):
# modify and return the request
r.headers["authorization"] = f"Bearer {self._token}"
return r


class JIRA:
"""User interface to Jira.
Expand Down Expand Up @@ -325,7 +338,8 @@ def __init__(
self,
server: str = None,
options: Dict[str, Union[str, bool, Any]] = None,
basic_auth: Union[None, Tuple[str, str]] = None,
basic_auth: Optional[Tuple[str, str]] = None,
token_auth: Optional[str] = None,
oauth: Dict[str, Any] = None,
jwt: Dict[str, Any] = None,
kerberos=False,
Expand All @@ -347,8 +361,8 @@ def __init__(
or ``atlas-run-standalone`` commands. By default, this instance runs at
``http://localhost:2990/jira``. The ``options`` argument can be used to set the Jira instance to use.
Authentication is handled with the ``basic_auth`` argument. If authentication is supplied (and is
accepted by Jira), the client will remember it for subsequent requests.
Authentication is handled with the ``basic_auth`` or ``token_auth`` argument.
If authentication is supplied (and is accepted by Jira), the client will remember it for subsequent requests.
For quick command line access to a server, see the ``jirashell`` script included with this distribution.
Expand All @@ -369,8 +383,11 @@ def __init__(
* check_update -- Check whether using the newest python-jira library version.
* headers -- a dict to update the default headers the session uses for all API requests.
basic_auth (Union[None, Tuple[str, str]]): A tuple of username and password to use when
basic_auth (Optional[Tuple[str, str]]): A tuple of username and password to use when
establishing a session via HTTP BASIC authentication.
token_auth (Optional[str]): A string containing the token necessary for (PAT) bearer token authorization.
oauth (Optional[Any]): A dict of properties for OAuth authentication. The following properties are required:
* access_token -- OAuth access token for the user
Expand Down Expand Up @@ -466,6 +483,8 @@ def __init__(
self._session.headers.update(self._options["headers"])
elif jwt:
self._create_jwt_session(jwt, timeout)
elif token_auth:
self._create_token_session(token_auth, timeout)
elif kerberos:
self._create_kerberos_session(timeout, kerberos_options=kerberos_options)
elif auth:
Expand Down Expand Up @@ -3412,6 +3431,20 @@ def _create_jwt_session(
self._session.verify = bool(self._options["verify"])
self._session.auth = jwt_auth

def _create_token_session(
self,
token_auth: str,
timeout: Optional[Union[Union[float, int], Tuple[float, float]]],
):
"""
Creates token-based session.
Header structure: "authorization": "Bearer <token_auth>"
"""
verify = self._options["verify"]
self._session = ResilientSession(timeout=timeout)
self._session.verify = verify
self._session.auth = TokenAuth(token_auth)

def _set_avatar(self, params, url, avatar):
data = {"id": avatar}
return self._session.put(url, params=params, data=json.dumps(data))
Expand Down
27 changes: 24 additions & 3 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ def prep():


@pytest.fixture(scope="module")
def test_manager():
def test_manager() -> JiraTestManager:
return JiraTestManager()


@pytest.fixture()
def cl_admin(test_manager):
def cl_admin(test_manager: JiraTestManager) -> jira.client.JIRA:
return test_manager.jira_admin


@pytest.fixture()
def cl_normal(test_manager):
def cl_normal(test_manager: JiraTestManager) -> jira.client.JIRA:
return test_manager.jira_normal


Expand Down Expand Up @@ -194,3 +194,24 @@ def test_headers_unclobbered_update_with_no_provided_headers(no_fields):

# THEN: we have not affected the other headers' defaults
assert session_headers[invariant_header_name] == invariant_header_value


def test_token_auth(cl_admin: jira.client.JIRA):
"""Tests the Personal Access Token authentication works."""
# GIVEN: We have a PAT token created by a user.
pat_token_request = {
"name": "my_new_token",
"expirationDuration": 1,
}
base_url = cl_admin.server_url
pat_token_response = cl_admin._session.post(
f"{base_url}/rest/pat/latest/tokens", json=pat_token_request
).json()
new_token = pat_token_response["rawToken"]

# WHEN: A new client is authenticated with this token
new_jira_client = jira.client.JIRA(token_auth=new_token)

# THEN: The reported authenticated user of the token
# matches the original token creator user.
assert cl_admin.myself() == new_jira_client.myself()

0 comments on commit 73d056e

Please sign in to comment.