-
Notifications
You must be signed in to change notification settings - Fork 62
OAuth app management API #138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
alrocar
wants to merge
6
commits into
master
Choose a base branch
from
135_oauth_app_api
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| """ | ||
| Module for working with CARTO OAuth app management API | ||
| https://carto.com/developers/oauth/apps/ | ||
| .. module:: carto.oauth_apps | ||
| :platform: Unix, Windows | ||
| :synopsis: Module for working with CARTO OAuth app management API | ||
| .. moduleauthor:: Alberto Romeu <[email protected]> | ||
| """ | ||
|
|
||
| from pyrestcli.fields import CharField, DateTimeField, BooleanField | ||
|
|
||
| from .resources import Resource, Manager | ||
| from .exceptions import CartoException | ||
| from .paginators import CartoPaginator | ||
|
|
||
|
|
||
| API_VERSION = "v4" | ||
| API_ENDPOINT = "api/{api_version}/oauth_apps/" | ||
| GRANTED_API_ENDPOINT = "api/{api_version}/granted_oauth_apps/" | ||
|
|
||
|
|
||
| class OauthApp(Resource): | ||
| """ | ||
| Represents an OAuth app in CARTO. | ||
| """ | ||
| id = CharField() | ||
| name = CharField() | ||
| client_id = CharField() | ||
| client_secret = CharField() | ||
| user_id = CharField() | ||
| user_name = CharField() | ||
| redirect_uris = CharField(many=True) | ||
| icon_url = CharField() | ||
| restricted = BooleanField() | ||
| created_at = DateTimeField() | ||
| updated_at = DateTimeField() | ||
|
|
||
| class Meta: | ||
| collection_endpoint = API_ENDPOINT.format(api_version=API_VERSION) | ||
| name_field = "id" | ||
|
|
||
| def regenerate_client_secret(self): | ||
| """ | ||
| Regenerates the associated client secret | ||
| :return: | ||
| :raise: CartoException | ||
| """ | ||
| try: | ||
| endpoint = (self.Meta.collection_endpoint | ||
| + "{id}/regenerate_secret"). \ | ||
| format(id=self.id) | ||
|
|
||
| self.send(endpoint, "POST") | ||
| except Exception as e: | ||
| raise CartoException(e) | ||
|
|
||
|
|
||
| class GrantedOauthApp(Resource): | ||
| """ | ||
| Represents an OAuth app granted to access a CARTO account. | ||
| """ | ||
| id = CharField() | ||
| name = CharField() | ||
| icon_url = CharField() | ||
| scopes = CharField(many=True) | ||
| created_at = DateTimeField() | ||
| updated_at = DateTimeField() | ||
|
|
||
| class Meta: | ||
| collection_endpoint = GRANTED_API_ENDPOINT.format(api_version=API_VERSION) | ||
| app_collection_endpoint = API_ENDPOINT.format(api_version=API_VERSION) | ||
| name_field = "id" | ||
|
|
||
| def revoke(self): | ||
| """ | ||
| Revokes the access of the OAuth app to the CARTO account of the user | ||
| :return: | ||
| :raise: CartoException | ||
| """ | ||
| try: | ||
| endpoint = (self.Meta.app_collection_endpoint | ||
| + "{id}/revoke"). \ | ||
| format(id=self.id) | ||
|
|
||
| self.send(endpoint, "POST") | ||
| except Exception as e: | ||
| raise CartoException(e) | ||
|
|
||
| def save(self): | ||
| pass | ||
|
|
||
| def refresh(self): | ||
| pass | ||
|
|
||
| def delete(self): | ||
| pass | ||
|
|
||
|
|
||
| class OauthAppManager(Manager): | ||
| """ | ||
| Manager for the OauthApp class. | ||
| """ | ||
| resource_class = OauthApp | ||
| json_collection_attribute = "result" | ||
| paginator_class = CartoPaginator | ||
|
|
||
| def create(self, name, redirect_uris, icon_url): | ||
| """ | ||
| Creates an OauthApp. | ||
| :param name: The OAuth app name | ||
| :param redirect_uris: An array of URIs for authorize callback. | ||
| :param icon_url: A URL with a squared icon for the Oauth app. | ||
| :type name: str | ||
| :type redirect_uris: list | ||
| :type icon_url: str | ||
| :return: An OauthApp instance with a client_id and client_secret | ||
| """ | ||
| return super(OauthAppManager, self).create(name=name, redirect_uris=redirect_uris, icon_url=icon_url) | ||
|
|
||
| def all_granted(self): | ||
| """ | ||
| Lists granted OAuth apps to access the user CARTO account. | ||
| :return: A list of GrantedOauthApp | ||
| """ | ||
| raw_resources = [] | ||
|
|
||
| for url, paginator_params in self.paginator.get_urls(GrantedOauthApp.Meta.collection_endpoint): | ||
| response = self.paginator.process_response(self.send(url, "get")) | ||
| raw_resources += self.client.get_response_data(response, self.Meta.parse_json)[self.json_collection_attribute] if self.json_collection_attribute is not None else self.client.get_response_data(response, self.Meta.parse_json) | ||
alrocar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| resources = [] | ||
|
|
||
| for raw_resource in raw_resources: | ||
| try: | ||
| resource = GrantedOauthApp(self.client) | ||
| except (ValueError, TypeError): | ||
| continue | ||
| else: | ||
| resource.update_from_dict(raw_resource) | ||
| resources.append(resource) | ||
|
|
||
| return resources | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import pytest | ||
| from time import time | ||
|
|
||
| from pyrestcli.exceptions import NotFoundException, UnprocessableEntityError | ||
|
|
||
| from carto.oauth_apps import OauthAppManager | ||
| from carto.exceptions import CartoException | ||
alrocar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def oauth_app_manager(api_key_auth_client_usr): | ||
| """ | ||
| Returns an OauthAppManager instance that can be reused in tests | ||
| :param oauth_app_auth_client: Fixture that provides a valid OauthAppAuthClient | ||
| object | ||
| :return: OauthAppManager instance | ||
| """ | ||
| return OauthAppManager(api_key_auth_client_usr) | ||
|
|
||
|
|
||
| def test_get_oauth_app_not_found(oauth_app_manager): | ||
| with pytest.raises(NotFoundException): | ||
| oauth_app_manager.get('non-existent') | ||
|
|
||
|
|
||
| def random_oauth_app_name(): | ||
| return '_'.join(str(time()).split('.')) | ||
|
|
||
|
|
||
| def create_oauth_app(oauth_app_manager, oauth_app_name=None, redirect_uris=['https://localhost']): | ||
| if oauth_app_name is None: | ||
| oauth_app_name = random_oauth_app_name() | ||
| return oauth_app_manager.create(name=oauth_app_name, redirect_uris=redirect_uris, icon_url='https://localhost') | ||
|
|
||
|
|
||
| def test_create_oauth_app(oauth_app_manager): | ||
| oauth_app = create_oauth_app(oauth_app_manager) | ||
| oauth_app_get = oauth_app_manager.get(oauth_app.id) | ||
| assert oauth_app.id == oauth_app_get.id | ||
| assert oauth_app.name == oauth_app_get.name | ||
| assert oauth_app.redirect_uris == oauth_app_get.redirect_uris | ||
| assert oauth_app.icon_url == oauth_app_get.icon_url | ||
| assert oauth_app.client_id is not None | ||
| assert oauth_app.client_secret is not None | ||
|
|
||
| oauth_app.delete() | ||
|
|
||
|
|
||
| def test_create_oauth_app_with_invalid_redirect_uris(oauth_app_manager): | ||
| with pytest.raises(UnprocessableEntityError): | ||
| create_oauth_app(oauth_app_manager, redirect_uris=['http://localhost']) | ||
|
|
||
|
|
||
| def test_regenerate_client_secret(oauth_app_manager): | ||
| oauth_app = create_oauth_app(oauth_app_manager) | ||
| old_client_secret = oauth_app.client_secret | ||
| oauth_app.regenerate_client_secret() | ||
| assert old_client_secret != oauth_app.client_secret | ||
|
|
||
| oauth_app.delete() | ||
|
|
||
|
|
||
| @pytest.mark.skipif(True, | ||
| reason="Execute manually eventually") | ||
| def test_revoke_granted(oauth_app_manager): | ||
| granted_oauth_apps = oauth_app_manager.all_granted() | ||
| old_count = len(granted_oauth_apps) | ||
| if len(granted_oauth_apps) > 0: | ||
| granted_oauth_apps[0].revoke() | ||
|
|
||
| granted_oauth_apps = oauth_app_manager.all_granted() | ||
| assert old_count > len(granted_oauth_apps) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/managemet/management/gc