From 44f49a43dd6539c64af4ed797907db103a088f07 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 10 Jun 2015 15:16:01 -0400 Subject: [PATCH] Fix creation of repositories when having a creator permission This fixes the grants on a user's session when creating a repository with only the creator permission Fixes #117 --- endpoints/index.py | 27 ++++++++++++++------------ initdb.py | 14 ++++++++++++++ test/registry_tests.py | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/endpoints/index.py b/endpoints/index.py index c89414724..96779cc06 100644 --- a/endpoints/index.py +++ b/endpoints/index.py @@ -34,7 +34,7 @@ class GrantType(object): WRITE_REPOSITORY = 'write' -def generate_headers(scope=GrantType.READ_REPOSITORY): +def generate_headers(scope=GrantType.READ_REPOSITORY, add_grant_for_status=None): def decorator_method(f): @wraps(f) def wrapper(namespace, repository, *args, **kwargs): @@ -49,15 +49,16 @@ def generate_headers(scope=GrantType.READ_REPOSITORY): response.headers['X-Docker-Endpoints'] = registry_server has_token_request = request.headers.get('X-Docker-Token', '') + force_grant = (add_grant_for_status == response.status_code) - if has_token_request: + if has_token_request or force_grant: grants = [] if scope == GrantType.READ_REPOSITORY: - if ReadRepositoryPermission(namespace, repository).can(): + if ReadRepositoryPermission(namespace, repository).can() or force_grant: grants.append(repository_read_grant(namespace, repository)) elif scope == GrantType.WRITE_REPOSITORY: - if ModifyRepositoryPermission(namespace, repository).can(): + if ModifyRepositoryPermission(namespace, repository).can() or force_grant: grants.append(repository_write_grant(namespace, repository)) # Generate a signed token for the user (if any) and the grants (if any) @@ -196,18 +197,18 @@ def update_user(username): @index.route('/repositories/', methods=['PUT']) @process_auth @parse_repository_name -@generate_headers(scope=GrantType.WRITE_REPOSITORY) +@generate_headers(scope=GrantType.WRITE_REPOSITORY, add_grant_for_status=201) @anon_allowed def create_repository(namespace, repository): - logger.debug('Parsing image descriptions') + logger.debug('Parsing image descriptions for repository %s/%s', namespace, repository) image_descriptions = json.loads(request.data.decode('utf8')) - logger.debug('Looking up repository') + logger.debug('Looking up repository %s/%s', namespace, repository) repo = model.get_repository(namespace, repository) - logger.debug('Repository looked up') + logger.debug('Found repository %s/%s', namespace, repository) if not repo and get_authenticated_user() is None: - logger.debug('Attempt to create new repository without user auth.') + logger.debug('Attempt to create repository %s/%s without user auth', namespace, repository) abort(401, message='Cannot create a repository as a guest. Please login via "docker login" first.', issue='no-login') @@ -219,17 +220,19 @@ def create_repository(namespace, repository): message='You do not have permission to modify repository %(namespace)s/%(repository)s', issue='no-repo-write-permission', namespace=namespace, repository=repository) - else: permission = CreateRepositoryPermission(namespace) if not permission.can(): - logger.info('Attempt to create a new repo with insufficient perms.') + logger.info('Attempt to create a new repo %s/%s with insufficient perms', namespace, repository) abort(403, message='You do not have permission to create repositories in namespace "%(namespace)s"', issue='no-create-permission', namespace=namespace) - logger.debug('Creaing repository with owner: %s', get_authenticated_user().username) + # Attempt to create the new repository. + logger.debug('Creating repository %s/%s with owner: %s', namespace, repository, + get_authenticated_user().username) + repo = model.create_repository(namespace, repository, get_authenticated_user()) diff --git a/initdb.py b/initdb.py index 0c35b4bf4..7f632ce66 100644 --- a/initdb.py +++ b/initdb.py @@ -354,6 +354,10 @@ def populate_database(): reader.verified = True reader.save() + creatoruser = model.create_user('creator', 'password', 'noc@thanks.com') + creatoruser.verified = True + creatoruser.save() + outside_org = model.create_user('outsideorg', 'password', 'no2@thanks.com') outside_org.verified = True outside_org.save() @@ -490,6 +494,9 @@ def populate_database(): model.create_robot('neworgrobot', org) + ownerbot = model.create_robot('ownerbot', org)[0] + creatorbot = model.create_robot('creatorbot', org)[0] + owners = model.get_organization_team('buynlarge', 'owners') owners.description = 'Owners have unfetterd access across the entire org.' owners.save() @@ -504,12 +511,19 @@ def populate_database(): [], (4, [], ['latest', 'prod'])) + creators = model.create_team('creators', org, 'creator', + 'Creators of orgrepo.') + reader_team = model.create_team('readers', org, 'member', 'Readers of orgrepo.') model.set_team_repo_permission(reader_team.name, org_repo.namespace_user.username, org_repo.name, 'read') + model.add_user_to_team(new_user_2, reader_team) model.add_user_to_team(reader, reader_team) + model.add_user_to_team(ownerbot, owners) + model.add_user_to_team(creatorbot, creators) + model.add_user_to_team(creatoruser, creators) __generate_repository(new_user_1, 'superwide', None, False, [], [(10, [], 'latest2'), diff --git a/test/registry_tests.py b/test/registry_tests.py index a2f98c067..3f7d3da41 100644 --- a/test/registry_tests.py +++ b/test/registry_tests.py @@ -346,5 +346,48 @@ class RegistryTests(RegistryTestCase): # Pull the repository as devtable, which should succeed because the repository is public. self.do_pull('public', 'newrepo', 'devtable', 'password') + + def test_create_repo_creator_user(self): + images = [{ + 'id': 'onlyimagehere' + }] + self.do_push('buynlarge', 'newrepo', 'creator', 'password', images) + + # Pull the repository as devtable, which should succeed because the repository is owned by the + # org. + self.do_pull('buynlarge', 'newrepo', 'devtable', 'password') + + + def test_create_repo_robot_owner(self): + # Lookup the robot's password. + self.conduct_api_login('devtable', 'password') + resp = self.conduct('GET', '/api/v1/organization/buynlarge/robots/ownerbot') + robot_token = json.loads(resp.text)['token'] + + images = [{ + 'id': 'onlyimagehere' + }] + self.do_push('buynlarge', 'newrepo', 'buynlarge+ownerbot', robot_token, images) + + # Pull the repository as devtable, which should succeed because the repository is owned by the + # org. + self.do_pull('buynlarge', 'newrepo', 'devtable', 'password') + + + def test_create_repo_robot_creator(self): + # Lookup the robot's password. + self.conduct_api_login('devtable', 'password') + resp = self.conduct('GET', '/api/v1/organization/buynlarge/robots/creatorbot') + robot_token = json.loads(resp.text)['token'] + + images = [{ + 'id': 'onlyimagehere' + }] + self.do_push('buynlarge', 'newrepo', 'buynlarge+creatorbot', robot_token, images) + + # Pull the repository as devtable, which should succeed because the repository is owned by the + # org. + self.do_pull('buynlarge', 'newrepo', 'devtable', 'password') + if __name__ == '__main__': unittest.main()