Skip to content

Commit

Permalink
fix: Fix deserialization logic (box/box-codegen#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
box-sdk-build committed Apr 26, 2024
1 parent 1b9628c commit e405fd8
Show file tree
Hide file tree
Showing 6 changed files with 13 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .codegen.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "engineHash": "baea184", "specHash": "1698c95", "version": "0.6.4" }
{ "engineHash": "f0bd1ce", "specHash": "1698c95", "version": "0.6.4" }
98 changes: 2 additions & 96 deletions box_sdk_gen/box/developer_token_auth.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
from typing import Optional

from typing import List

from box_sdk_gen.schemas import PostOAuth2TokenGrantTypeField

from box_sdk_gen.schemas import PostOAuth2TokenSubjectTokenTypeField

from box_sdk_gen.schemas import AccessToken

from box_sdk_gen.networking.auth import Authentication
Expand All @@ -14,37 +8,11 @@

from box_sdk_gen.box.errors import BoxSDKError

from box_sdk_gen.box.token_storage import TokenStorage

from box_sdk_gen.box.token_storage import InMemoryTokenStorage

from box_sdk_gen.managers.authorization import AuthorizationManager

from box_sdk_gen.schemas import PostOAuth2Token

from box_sdk_gen.schemas import PostOAuth2Revoke


class DeveloperTokenConfig:
def __init__(
self, *, client_id: Optional[str] = None, client_secret: Optional[str] = None
):
self.client_id = client_id
self.client_secret = client_secret


class BoxDeveloperTokenAuth(Authentication):
def __init__(self, token: str, *, config: DeveloperTokenConfig = None, **kwargs):
"""
:param config: Configuration object of DeveloperTokenAuth., defaults to None
:type config: DeveloperTokenConfig, optional
"""
def __init__(self, token: str, **kwargs):
super().__init__(**kwargs)
self.token = token
self.config = config
self.token_storage = InMemoryTokenStorage(
token=AccessToken(access_token=self.token)
)

def retrieve_token(
self, *, network_session: Optional[NetworkSession] = None
Expand All @@ -54,10 +22,7 @@ def retrieve_token(
:param network_session: An object to keep network session state, defaults to None
:type network_session: Optional[NetworkSession], optional
"""
token: Optional[AccessToken] = self.token_storage.get()
if token == None:
raise BoxSDKError(message='No access token is available.')
return token
return AccessToken(access_token=self.token)

def refresh_token(
self, *, network_session: Optional[NetworkSession] = None
Expand All @@ -76,62 +41,3 @@ def retrieve_authorization_header(
) -> str:
token: AccessToken = self.retrieve_token(network_session=network_session)
return ''.join(['Bearer ', token.access_token])

def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None:
"""
Revoke an active Access Token, effectively logging a user out that has been previously authenticated.
:param network_session: An object to keep network session state, defaults to None
:type network_session: Optional[NetworkSession], optional
"""
token: Optional[AccessToken] = self.token_storage.get()
if token == None:
return None
auth_manager: AuthorizationManager = AuthorizationManager(
network_session=(
network_session if not network_session == None else NetworkSession()
)
)
auth_manager.revoke_access_token(
client_id=self.config.client_id,
client_secret=self.config.client_secret,
token=token.access_token,
)
self.token_storage.clear()
return None

def downscope_token(
self,
scopes: List[str],
*,
resource: Optional[str] = None,
shared_link: Optional[str] = None,
network_session: Optional[NetworkSession] = None
) -> AccessToken:
"""
Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged.
:param scopes: The scope(s) to apply to the resulting token.
:type scopes: List[str]
:param resource: The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456., defaults to None
:type resource: Optional[str], optional
:param shared_link: The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item., defaults to None
:type shared_link: Optional[str], optional
:param network_session: An object to keep network session state, defaults to None
:type network_session: Optional[NetworkSession], optional
"""
token: Optional[AccessToken] = self.token_storage.get()
if token == None or token.access_token == None:
raise BoxSDKError(message='No access token is available.')
auth_manager: AuthorizationManager = AuthorizationManager(
network_session=(
network_session if not network_session == None else NetworkSession()
)
)
downscoped_token: AccessToken = auth_manager.request_access_token(
PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE.value,
subject_token=token.access_token,
subject_token_type=PostOAuth2TokenSubjectTokenTypeField.URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN.value,
resource=resource,
scope=' '.join(scopes),
box_shared_link=shared_link,
)
return downscoped_token
10 changes: 5 additions & 5 deletions box_sdk_gen/box/token_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ def clear(self) -> None:


class InMemoryTokenStorage(TokenStorage):
def __init__(self, token: Optional[AccessToken] = None):
self._token = token
def __init__(self):
self.token: Optional[AccessToken] = None

def store(self, token: AccessToken) -> None:
self._token = token
self.token = token

def get(self) -> Optional[AccessToken]:
return self._token
return self.token

def clear(self) -> None:
self._token = None
self.token = None


class FileTokenStorage(TokenStorage):
Expand Down
17 changes: 0 additions & 17 deletions box_sdk_gen/networking/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from abc import abstractmethod

from typing import List

from box_sdk_gen.schemas import AccessToken

from box_sdk_gen.networking.network import NetworkSession
Expand All @@ -30,18 +28,3 @@ def retrieve_authorization_header(
self, *, network_session: Optional[NetworkSession] = None
) -> str:
pass

@abstractmethod
def revoke_token(self, *, network_session: Optional[NetworkSession] = None) -> None:
pass

@abstractmethod
def downscope_token(
self,
scopes: List[str],
*,
resource: Optional[str] = None,
shared_link: Optional[str] = None,
network_session: Optional[NetworkSession] = None
) -> AccessToken:
pass
12 changes: 5 additions & 7 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,9 @@ if __name__ == '__main__':
# Revoke token

Access tokens for a client can be revoked when needed. This call invalidates old token.
For BoxCCGAuth and BoxJWTAuth you can still reuse the `auth` object to retrieve a new token.
If you make any new call after revoking the token, a new token will be automatically retrieved.
For BoxOAuth it would be necessary to manually go through the authentication process again.
For BoxDeveloperTokenAuth, it is necessary to provide a DeveloperTokenConfig during initialization,
containing the client ID and client secret.
For CCGAuth and JWTAuth you can still reuse the `auth` object to retrieve a new token. If you make any new call after revoking the token,
a new token will be automatically retrieved.
For OAuth it would be necessary to manually go through the authentication process again.

To revoke current client's tokens in the storage use the following code:

Expand All @@ -344,7 +342,7 @@ If you want to learn more about available scopes please go [here](https://develo

For example to get a new token with only `item_preview` scope, restricted to a single file, suitable for the
[Content Preview UI Element](https://developer.box.com/en/guides/embed/ui-elements/preview/) you can use the following code.
You can also initialize `BoxDeveloperTokenAuth` with the retrieved access token and use it to create a new Client.
You can also initialize `DeveloperTokenAuth` with the retrieved access token and use it to create a new Client.

<!-- sample post_oauth2_token downscope_token -->

Expand All @@ -356,7 +354,7 @@ downscoped_token: AccessToken = auth.downscope_token(
scopes=['item_preview'],
resource=resource,
)
downscoped_auth = BoxDeveloperTokenAuth(token=downscoped_token.access_token)
downscoped_auth = BoxDeveloperTokenAuth(downscoped_token.access_token)
client = BoxClient(auth=downscoped_auth)
```

Expand Down
44 changes: 0 additions & 44 deletions test/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@

from box_sdk_gen.box.developer_token_auth import BoxDeveloperTokenAuth

from box_sdk_gen.box.developer_token_auth import DeveloperTokenConfig

from box_sdk_gen.box.oauth import BoxOAuth

from box_sdk_gen.box.oauth import OAuthConfig
Expand Down Expand Up @@ -202,48 +200,6 @@ def get_access_token() -> AccessToken:
return auth_user.retrieve_token()


def test_developer_token_auth_revoke():
developer_token_config: DeveloperTokenConfig = DeveloperTokenConfig(
client_id=get_env_var('CLIENT_ID'), client_secret=get_env_var('CLIENT_SECRET')
)
token: AccessToken = get_access_token()
auth: BoxDeveloperTokenAuth = BoxDeveloperTokenAuth(
token=token.access_token, config=developer_token_config
)
auth.retrieve_token()
token_from_storage_before_revoke: Optional[AccessToken] = auth.token_storage.get()
auth.revoke_token()
token_from_storage_after_revoke: Optional[AccessToken] = auth.token_storage.get()
assert not token_from_storage_before_revoke == None
assert token_from_storage_after_revoke == None


def test_developer_token_auth_downscope():
developer_token_config: DeveloperTokenConfig = DeveloperTokenConfig(
client_id=get_env_var('CLIENT_ID'), client_secret=get_env_var('CLIENT_SECRET')
)
token: AccessToken = get_access_token()
auth: BoxDeveloperTokenAuth = BoxDeveloperTokenAuth(
token=token.access_token, config=developer_token_config
)
parent_client: BoxClient = BoxClient(auth=auth)
folder: FolderFull = parent_client.folders.create_folder(
get_uuid(), CreateFolderParent(id='0')
)
resource_path: str = ''.join(['https://api.box.com/2.0/folders/', folder.id])
downscoped_token: AccessToken = auth.downscope_token(
['item_rename', 'item_preview'], resource=resource_path
)
assert not downscoped_token.access_token == None
downscoped_client: BoxClient = BoxClient(
auth=BoxDeveloperTokenAuth(token=downscoped_token.access_token)
)
downscoped_client.folders.update_folder_by_id(folder.id, name=get_uuid())
with pytest.raises(Exception):
downscoped_client.folders.delete_folder_by_id(folder.id)
parent_client.folders.delete_folder_by_id(folder.id)


def test_developer_token_auth():
user_id: str = get_env_var('USER_ID')
token: AccessToken = get_access_token()
Expand Down

0 comments on commit e405fd8

Please sign in to comment.