From 8ab600707cd7000690675e02d0faf28fb79102d5 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 20 Jul 2017 17:07:01 -0400 Subject: [PATCH] Change repotoken to use a data interface --- endpoints/api/repotoken.py | 72 ++++++++------------- endpoints/api/repotoken_models_interface.py | 60 +++++++++++++++++ endpoints/api/repotoken_models_pre_oci.py | 33 ++++++++++ 3 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 endpoints/api/repotoken_models_interface.py create mode 100644 endpoints/api/repotoken_models_pre_oci.py diff --git a/endpoints/api/repotoken.py b/endpoints/api/repotoken.py index 5ef427bf9..b0c1faa23 100644 --- a/endpoints/api/repotoken.py +++ b/endpoints/api/repotoken.py @@ -6,21 +6,11 @@ from flask import request from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource, log_action, validate_json_request, path_param) +from endpoints.api.repotoken_models_pre_oci import pre_oci_model as model from endpoints.exception import NotFound -from data import model - logger = logging.getLogger(__name__) - -def token_view(token_obj): - return { - 'friendlyName': token_obj.friendly_name, - 'code': token_obj.code, - 'role': token_obj.role.name, - } - - @resource('/v1/repository//tokens/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryTokenList(RepositoryParamResource): @@ -43,28 +33,25 @@ class RepositoryTokenList(RepositoryParamResource): @require_repo_admin @nickname('listRepoTokens') - def get(self, namespace, repository): + def get(self, namespace_name, repo_name): """ List the tokens for the specified repository. """ - tokens = model.token.get_repository_delegate_tokens(namespace, repository) - + tokens = model.get_repository_tokens(namespace_name, repo_name) return { - 'tokens': {token.code: token_view(token) for token in tokens} + 'tokens': {token.code: token.to_dict() for token in tokens} } @require_repo_admin @nickname('createToken') @validate_json_request('NewToken') - def post(self, namespace, repository): + def post(self, namespace_name, repo_name): """ Create a new repository token. """ token_params = request.get_json() + token = model.create_repository_token(namespace_name, repo_name, token_params['friendlyName']) + log_action('add_repo_accesstoken', namespace_name, + {'repo': repo_name, 'token': token_params['friendlyName']}, + repo_name=repo_name) - token = model.token.create_delegate_token(namespace, repository, token_params['friendlyName']) - - log_action('add_repo_accesstoken', namespace, - {'repo': repository, 'token': token_params['friendlyName']}, - repo=model.repository.get_repository(namespace, repository)) - - return token_view(token), 201 + return token.to_dict(), 201 @resource('/v1/repository//tokens/') @@ -92,46 +79,41 @@ class RepositoryToken(RepositoryParamResource): }, }, } + @require_repo_admin @nickname('getTokens') - def get(self, namespace, repository, code): + def get(self, namespace_name, repo_name, code): """ Fetch the specified repository token information. """ - try: - perm = model.token.get_repo_delegate_token(namespace, repository, code) - except model.InvalidTokenException: + token = model.get_repository_token(namespace_name, repo_name, code) + if token is None: raise NotFound() - return token_view(perm) + return token.to_dict() @require_repo_admin @nickname('changeToken') @validate_json_request('TokenPermission') - def put(self, namespace, repository, code): + def put(self, namespace_name, repo_name, code): """ Update the permissions for the specified repository token. """ new_permission = request.get_json() + logger.debug('Setting permission to: %s for code %s', new_permission['role'], code) - logger.debug('Setting permission to: %s for code %s' % - (new_permission['role'], code)) - - token = model.token.set_repo_delegate_token_role(namespace, repository, code, - new_permission['role']) - - log_action('change_repo_permission', namespace, - {'repo': repository, 'token': token.friendly_name, 'code': code, + token = model.set_repository_token_role(namespace_name, repo_name, code, new_permission['role']) + log_action('change_repo_permission', namespace_name, + {'repo': repo_name, 'token': token.friendly_name, 'code': code, 'role': new_permission['role']}, - repo=model.repository.get_repository(namespace, repository)) + repo_name=repo_name) - return token_view(token) + return token.to_dict() @require_repo_admin @nickname('deleteToken') - def delete(self, namespace, repository, code): + def delete(self, namespace_name, repo_name, code): """ Delete the repository token. """ - token = model.token.delete_delegate_token(namespace, repository, code) - - log_action('delete_repo_accesstoken', namespace, - {'repo': repository, 'token': token.friendly_name, + token = model.delete_repository_token(namespace_name, repo_name, code) + log_action('delete_repo_accesstoken', namespace_name, + {'repo': repo_name, 'token': token.friendly_name, 'code': code}, - repo=model.repository.get_repository(namespace, repository)) + repo_name=repo_name) return '', 204 diff --git a/endpoints/api/repotoken_models_interface.py b/endpoints/api/repotoken_models_interface.py new file mode 100644 index 000000000..b5bc17621 --- /dev/null +++ b/endpoints/api/repotoken_models_interface.py @@ -0,0 +1,60 @@ +from abc import ABCMeta, abstractmethod +from collections import namedtuple +from six import add_metaclass + +class RepositoryToken(namedtuple('RepositoryToken', ['friendly_name', 'code', 'role'])): + """ + RepositoryToken represents a (deprecated) repository access token. + :type friendly_name: string + :type code: string + :type role: string + """ + + def to_dict(self): + return { + 'friendlyName': self.friendly_name, + 'code': self.code, + 'role': self.role.name, + } + + +@add_metaclass(ABCMeta) +class RepoTokenDataInterface(object): + """ + Interface that represents all data store interactions required by the repository tokens API. + Note that this API is *deprecated*. + """ + + @abstractmethod + def get_repository_tokens(self, namespace_name, repository_name): + """ + Returns an iterator of all the RepositoryToken's found for the repository with the given + namespace and name. If the repository doesn't exist, an empty iterator is returned. + """ + + @abstractmethod + def create_repository_token(self, namespace_name, repository_name, friendly_name): + """ + Creates and returns a new RepositoryToken under the matching repository. + """ + + @abstractmethod + def get_repository_token(self, namespace_name, repository_name, code): + """ + Returns the repository access token with the given code under the matching repository or None + if none. + """ + + @abstractmethod + def delete_repository_token(self, namespace_name, repository_name, code): + """ + Deletes the repository access token with the given code under the matching repository, if any. + Returns the token deleted or None if none. + """ + + @abstractmethod + def set_repository_token_role(self, namespace_name, repository_name, code, role_name): + """ + Sets the role of the repository token with the given code under the matching repository, to the + role given. + """ diff --git a/endpoints/api/repotoken_models_pre_oci.py b/endpoints/api/repotoken_models_pre_oci.py new file mode 100644 index 000000000..a5dee37c5 --- /dev/null +++ b/endpoints/api/repotoken_models_pre_oci.py @@ -0,0 +1,33 @@ +from data import model +from endpoints.api.repotoken_models_interface import RepoTokenDataInterface, RepositoryToken + +def _token(t): + return RepositoryToken(friendly_name=t.friendly_name, code=t.code, role=t.role) + +class RepoTokenPreOCIModel(RepoTokenDataInterface): + def get_repository_tokens(self, namespace_name, repository_name): + tokens = model.token.get_repository_delegate_tokens(namespace_name, repository_name) + return [_token(t) for t in tokens] + + def create_repository_token(self, namespace_name, repository_name, friendly_name): + token = model.token.create_delegate_token(namespace_name, repository_name, friendly_name) + return _token(token) + + def get_repository_token(self, namespace_name, repository_name, code): + try: + token = model.token.get_repo_delegate_token(namespace_name, repository_name, code) + return _token(token) + except model.InvalidTokenException: + return None + + def delete_repository_token(self, namespace_name, repository_name, code): + token = model.token.delete_delegate_token(namespace_name, repository_name, code) + return _token(token) + + def set_repository_token_role(self, namespace_name, repository_name, code, role_name): + token = model.token.set_repo_delegate_token_role(namespace_name, repository_name, code, + role_name) + return _token(token) + + +pre_oci_model = RepoTokenPreOCIModel()