From 2f11d9635464a6d957ce7baf36dc38ad7106661f Mon Sep 17 00:00:00 2001 From: ahongbynder Date: Mon, 13 Jan 2025 11:39:12 -0800 Subject: [PATCH 1/5] API-2197 check for client credentials, use corresponding client in oauth library --- bynder_sdk/client/bynder_client.py | 7 ++++++- samples/client.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bynder_sdk/client/bynder_client.py b/bynder_sdk/client/bynder_client.py index cfbaea1..07443a4 100644 --- a/bynder_sdk/client/bynder_client.py +++ b/bynder_sdk/client/bynder_client.py @@ -4,6 +4,7 @@ from bynder_sdk.client.workflow_client import WorkflowClient from bynder_sdk.oauth2 import BynderOAuth2Session from bynder_sdk.permanent_token import PermanentTokenSession +from oauthlib.oauth2 import BackendApplicationClient REQUIRED_OAUTH_KWARGS = ( 'client_id', 'client_secret', 'redirect_uri', 'scopes') @@ -29,6 +30,8 @@ def __init__(self, domain, **kwargs): f'Missing required arguments: {missing}' ) + # if client credentials use BackendApplicationClient from oauthlib, client suited for client credentials + client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs['client_credentials'] else None self.session = BynderOAuth2Session( domain, kwargs['client_id'], @@ -38,7 +41,9 @@ def __init__(self, domain, **kwargs): 'client_id': kwargs['client_id'], 'client_secret': kwargs['client_secret'] }, - token_updater=kwargs.get('token_saver', (lambda _: None)) + token_updater=kwargs.get('token_saver', (lambda _: None)), + # if client is None, default to WebApplicationClient which uses authorization_code grant type + client=client_credentials ) if kwargs.get('token') is not None: diff --git a/samples/client.py b/samples/client.py index d18276b..ac18cb4 100644 --- a/samples/client.py +++ b/samples/client.py @@ -39,10 +39,16 @@ def get_auth_client(self) -> BynderClient: # "scope": ["offline"], # "token_type": "bearer" # } - if self.config_data.get('token', None) is None: + + # auth code grant type + if self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None) is None: print(bynder_client.get_authorization_url()) code = input('Code: ') print(bynder_client.fetch_token(code)) - return bynder_client \ No newline at end of file + # client credentials grant type + elif self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None): + bynder_client.fetch_token(code=None) + + return bynder_client From 49b711a7bbfd1496e7cfe426da4fae237601b79f Mon Sep 17 00:00:00 2001 From: ahongbynder Date: Mon, 13 Jan 2025 11:47:37 -0800 Subject: [PATCH 2/5] API-2197 check for client credentials key --- bynder_sdk/client/bynder_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bynder_sdk/client/bynder_client.py b/bynder_sdk/client/bynder_client.py index 07443a4..29a1317 100644 --- a/bynder_sdk/client/bynder_client.py +++ b/bynder_sdk/client/bynder_client.py @@ -31,7 +31,7 @@ def __init__(self, domain, **kwargs): ) # if client credentials use BackendApplicationClient from oauthlib, client suited for client credentials - client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs['client_credentials'] else None + client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs.get('client_credentials', None) else None self.session = BynderOAuth2Session( domain, kwargs['client_id'], From ae639ddc63fa1f400d408b67215ef1bf2fb581fe Mon Sep 17 00:00:00 2001 From: ahongbynder Date: Tue, 14 Jan 2025 09:30:40 -0800 Subject: [PATCH 3/5] API-2197 check for client credentials key and boolean value --- bynder_sdk/client/bynder_client.py | 2 +- samples/client.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bynder_sdk/client/bynder_client.py b/bynder_sdk/client/bynder_client.py index 29a1317..4d73bae 100644 --- a/bynder_sdk/client/bynder_client.py +++ b/bynder_sdk/client/bynder_client.py @@ -31,7 +31,7 @@ def __init__(self, domain, **kwargs): ) # if client credentials use BackendApplicationClient from oauthlib, client suited for client credentials - client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs.get('client_credentials', None) else None + client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs.get('client_credentials', None) == True else None self.session = BynderOAuth2Session( domain, kwargs['client_id'], diff --git a/samples/client.py b/samples/client.py index ac18cb4..2463477 100644 --- a/samples/client.py +++ b/samples/client.py @@ -41,14 +41,14 @@ def get_auth_client(self) -> BynderClient: # } # auth code grant type - if self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None) is None: + if self.config_data.get('token', None) is None and not self.config_data.get('client_credentials', None): print(bynder_client.get_authorization_url()) code = input('Code: ') print(bynder_client.fetch_token(code)) # client credentials grant type - elif self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None): + elif self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None) == True: bynder_client.fetch_token(code=None) return bynder_client From 342767849eb54be4c6d8cd9a1f3963290eb612e9 Mon Sep 17 00:00:00 2001 From: ahongbynder Date: Tue, 14 Jan 2025 11:45:21 -0800 Subject: [PATCH 4/5] API-2197 update README for client credentials usage --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 58c1434..0dd600f 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,36 @@ bynder_client = BynderClient( permanent_token='' ) ``` +To use client credentials grant type, add the field `client_credentials` to `secret.json` and set the field to true. + +```json +{ + "domain": "*****", + "client_id": "*****", + "client_secret": "******", + "client_credentials": true, + "scopes": ["asset.usage:write", "collection:write", "meta.workflow:read", "asset:write", "asset:read", "meta.assetbank:write", "collection:read", "admin.user:read", "meta.assetbank:read", "current.user:read", "current.profile:read", "offline", "admin.profile:read", "asset.usage:read", "admin.user:write"] +} +``` +``` python +# client credentials grant type +elif self.config_data.get('token', None) is None and self.config_data.get('client_credentials', None) == True: + bynder_client.fetch_token(code=None) +``` + +```python +bynder_client = BynderClient( + **self.config_data, + token_saver=self.token_saver, # optional, defaults to empty lambda +) +``` +Client credentials provided as keyword argument: +```python +bynder_client = BynderClient( + client_credentials=True, + token_saver=self.token_saver, # optional, defaults to empty lambda +) +``` Finally call one of the API's endpoints through one of the clients: From 797881c5d11f52860f2e3a1c627ba109b10b3d88 Mon Sep 17 00:00:00 2001 From: ahongbynder Date: Thu, 16 Jan 2025 08:35:54 -0800 Subject: [PATCH 5/5] API-2197 add Makefile command to update container package, remove checks for scopes/redirect uri for client credentials --- Makefile | 6 +++++- README.md | 7 +++++++ bynder_sdk/client/bynder_client.py | 23 ++++++++++++++++------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 2b9b007..7b4ae17 100644 --- a/Makefile +++ b/Makefile @@ -48,4 +48,8 @@ run-docker: .PHONY: stop-docker stop-docker: - docker-compose down \ No newline at end of file + docker-compose down + +.PHONY: update-container-package +update-container-package: + docker-compose exec bynder-python-sdk sh -c "pip install -e ." \ No newline at end of file diff --git a/README.md b/README.md index 0dd600f..5d49224 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,13 @@ All sample files are located in the `./samples` directory. > :warning: Caution: The sample scripts are provided as examples. It is crucial to review, add and/or modify the commands before execution. The container updates automatically with changes, ensuring a seamless development experience. Always exercise caution when executing scripts. +Update the package within container to reflect local changes by running + +```bash +make update-container-package +``` +This will run `pip install -e .` inside the container to use local package changes. + ## Stopping the Docker Container When you're done with your development or testing, you can stop the Docker container using the following command: diff --git a/bynder_sdk/client/bynder_client.py b/bynder_sdk/client/bynder_client.py index 4d73bae..ecb1e8c 100644 --- a/bynder_sdk/client/bynder_client.py +++ b/bynder_sdk/client/bynder_client.py @@ -9,6 +9,8 @@ REQUIRED_OAUTH_KWARGS = ( 'client_id', 'client_secret', 'redirect_uri', 'scopes') +REQUIRED_OAUTH_KWARGS_CLIENT_CREDENTIALS = ('client_id', 'client_secret') + class BynderClient: """ Main client used for setting up the OAuth2 session and @@ -21,22 +23,29 @@ def __init__(self, domain, **kwargs): self.session = PermanentTokenSession( domain, kwargs['permanent_token']) else: - missing = [ - kw for kw in REQUIRED_OAUTH_KWARGS - if kwargs.get(kw) is None - ] + if kwargs.get('client_credentials', None): + missing = [ + kw for kw in REQUIRED_OAUTH_KWARGS_CLIENT_CREDENTIALS + if kwargs.get(kw) is None + ] + else: + missing = [ + kw for kw in REQUIRED_OAUTH_KWARGS + if kwargs.get(kw) is None + ] + if missing: raise TypeError( f'Missing required arguments: {missing}' ) # if client credentials use BackendApplicationClient from oauthlib, client suited for client credentials - client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs.get('client_credentials', None) == True else None + client_credentials = BackendApplicationClient(kwargs['client_id']) if kwargs.get('client_credentials', None) else None self.session = BynderOAuth2Session( domain, kwargs['client_id'], - scope=kwargs['scopes'], - redirect_uri=kwargs['redirect_uri'], + scope=kwargs['scopes'] if kwargs.get('scopes', None) else None, + redirect_uri=kwargs['redirect_uri'] if kwargs.get('redirect_uri', None) else None, auto_refresh_kwargs={ 'client_id': kwargs['client_id'], 'client_secret': kwargs['client_secret']