Skip to content

Commit

Permalink
fix: support new create access token API
Browse files Browse the repository at this point in the history
  • Loading branch information
anancarv authored and Ananias Carvalho committed Sep 20, 2024
1 parent e3fb56d commit 446f111
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 49 deletions.
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,9 @@ art.security.create_api_key( art.security.get_encrypted_password( art.
art.security.get_api_key( art.security.regenerate_api_key( art.security.revoke_user_api_key(
```

Create an access token (for a transient user):
Create an access token:
```python
token = art.security.create_access_token(user_name='transient_artifactory_user',
groups=['g1', 'g2'],
refreshable=True)
```

Create an access token for an existing user (groups are implied from the existing user):
```python
token = art.security.create_access_token(user_name='existing_artifactory_user',
refreshable=True)
token = art.security.create_access_token(user_name='artifactory_user', refreshable=True, scope="applied-permissions/user")
```

Revoke an existing revocable token:
Expand Down
35 changes: 12 additions & 23 deletions pyartifactory/objects/security.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import logging
from typing import Dict, Optional

from pyartifactory.exception import InvalidTokenDataError
from pyartifactory.models.auth import AccessTokenModel, ApiKeyModel, PasswordModel
Expand All @@ -14,6 +13,7 @@ class ArtifactorySecurity(ArtifactoryObject):
"""Models artifactory security."""

_uri = "security"
_tokens_uri = "tokens"

def get_encrypted_password(self) -> PasswordModel:
"""
Expand All @@ -29,7 +29,7 @@ def create_access_token(
user_name: str,
expires_in: int = 3600,
refreshable: bool = False,
groups: Optional[str] = None,
scope: str = "applied-permissions/user",
) -> AccessTokenModel:
"""
Creates an access token.
Expand All @@ -38,39 +38,28 @@ def create_access_token(
is created if user doesn't exist in artifactory.
:param expires_in: Expiry time for the token in seconds. For eternal tokens specify 0.
:param refreshable: If set to true token can be refreshed using the refresh token returned.
:param groups: A list of groups the token has membership of.
If an existing user in artifactory is used with existing memberships
groups are automatically implied without specification.
:param scope: The scope of access that the token provides.
:return: AccessToken
"""
payload = {
"username": user_name,
"expires_in": expires_in,
"refreshable": refreshable,
}
if groups:
if not isinstance(groups, list):
raise ValueError(groups)
scope = f'member-of-groups:"{",".join(groups)}"'
payload.update({"scope": scope})
response = self._post(f"api/{self._uri}/token", data=payload, raise_for_status=False)
payload = {"username": user_name, "expires_in": expires_in, "refreshable": refreshable, "scope": scope}
response = self._post(f"access/api/v1/{self._tokens_uri}", data=payload, raise_for_status=False)
if response.ok:
return AccessTokenModel(**response.json())
raise InvalidTokenDataError(response.json().get("error_description", "Unknown error"))

def revoke_access_token(self, token: Optional[str] = None, token_id: Optional[str] = None) -> bool:
def revoke_access_token(self, token: str) -> bool:
"""
Revokes an access token.
:param token: The token to revoke
:param token_id: The id of a token to revoke
:return: bool True or False indicating success or failure of token revocation attempt.
"""
if not any([token, token_id]):
logger.error("Neither a token or a token id was specified")
raise InvalidTokenDataError
payload: Dict[str, Optional[str]] = {"token": token} if token else {"token_id": token_id}
response = self._post(f"api/{self._uri}/token/revoke", data=payload, raise_for_status=False)

response = self._delete(
f"access/api/v1/{self._tokens_uri}/revoke",
data={"token": token},
raise_for_status=False,
)
if response.ok:
logger.debug("Token revoked successfully, or token did not exist")
return True
Expand Down
25 changes: 9 additions & 16 deletions tests/test_security.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from __future__ import annotations

import pytest
import responses

from pyartifactory import ArtifactorySecurity
from pyartifactory.exception import InvalidTokenDataError
from pyartifactory.models import ApiKeyModel, AuthModel, PasswordModel

URL = "http://localhost:8080/artifactory"
Expand Down Expand Up @@ -77,29 +75,24 @@ def test_revoke_user_api_key():
def test_create_access_token():
responses.add(
responses.POST,
f"{URL}/api/security/token",
f"{URL}/access/api/v1/tokens",
status=200,
json={
"access_token": "<the access token>",
"expires_in": 3600,
"scope": "api:* member-of-groups:g1, g2",
"token_type": "Bearer",
"scope": "applied-permissions/user",
"token_type": "access_token",
},
)

artifactory_security = ArtifactorySecurity(AuthModel(url=URL, auth=AUTH))
access_token = artifactory_security.create_access_token(
user_name="my-username",
expires_in=3600,
refreshable=False,
groups=["g1", "g2"],
)
assert access_token.scope == "api:* member-of-groups:g1, g2"
access_token = artifactory_security.create_access_token(user_name="my-username", expires_in=3600, refreshable=False)
assert access_token.scope == "applied-permissions/user"


@responses.activate
def test_revoke_access_token_success():
responses.add(responses.POST, f"{URL}/api/security/token/revoke", status=200)
responses.add(responses.DELETE, f"{URL}/access/api/v1/tokens/revoke", status=200)

artifactory_security = ArtifactorySecurity(AuthModel(url=URL, auth=AUTH))
result = artifactory_security.revoke_access_token(token="my-token") # noqa: S106
Expand All @@ -108,8 +101,8 @@ def test_revoke_access_token_success():

@responses.activate
def test_revoke_access_token_fail_no_token_provided():
responses.add(responses.POST, f"{URL}/api/security/token/revoke", status=400)
responses.add(responses.DELETE, f"{URL}/access/api/v1/tokens/revoke", status=400)

artifactory_security = ArtifactorySecurity(AuthModel(url=URL, auth=AUTH))
with pytest.raises(InvalidTokenDataError):
artifactory_security.revoke_access_token()
result = artifactory_security.revoke_access_token(token="my-token") # noqa: S106
assert result is False

0 comments on commit 446f111

Please sign in to comment.