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
This commit is contained in:
parent
b7303665a2
commit
44f49a43dd
3 changed files with 72 additions and 12 deletions
|
@ -34,7 +34,7 @@ class GrantType(object):
|
||||||
WRITE_REPOSITORY = 'write'
|
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):
|
def decorator_method(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(namespace, repository, *args, **kwargs):
|
def wrapper(namespace, repository, *args, **kwargs):
|
||||||
|
@ -49,15 +49,16 @@ def generate_headers(scope=GrantType.READ_REPOSITORY):
|
||||||
response.headers['X-Docker-Endpoints'] = registry_server
|
response.headers['X-Docker-Endpoints'] = registry_server
|
||||||
|
|
||||||
has_token_request = request.headers.get('X-Docker-Token', '')
|
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 = []
|
grants = []
|
||||||
|
|
||||||
if scope == GrantType.READ_REPOSITORY:
|
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))
|
grants.append(repository_read_grant(namespace, repository))
|
||||||
elif scope == GrantType.WRITE_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))
|
grants.append(repository_write_grant(namespace, repository))
|
||||||
|
|
||||||
# Generate a signed token for the user (if any) and the grants (if any)
|
# 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/<path:repository>', methods=['PUT'])
|
@index.route('/repositories/<path:repository>', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(scope=GrantType.WRITE_REPOSITORY)
|
@generate_headers(scope=GrantType.WRITE_REPOSITORY, add_grant_for_status=201)
|
||||||
@anon_allowed
|
@anon_allowed
|
||||||
def create_repository(namespace, repository):
|
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'))
|
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)
|
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:
|
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,
|
abort(401,
|
||||||
message='Cannot create a repository as a guest. Please login via "docker login" first.',
|
message='Cannot create a repository as a guest. Please login via "docker login" first.',
|
||||||
issue='no-login')
|
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',
|
message='You do not have permission to modify repository %(namespace)s/%(repository)s',
|
||||||
issue='no-repo-write-permission',
|
issue='no-repo-write-permission',
|
||||||
namespace=namespace, repository=repository)
|
namespace=namespace, repository=repository)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
permission = CreateRepositoryPermission(namespace)
|
permission = CreateRepositoryPermission(namespace)
|
||||||
if not permission.can():
|
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,
|
abort(403,
|
||||||
message='You do not have permission to create repositories in namespace "%(namespace)s"',
|
message='You do not have permission to create repositories in namespace "%(namespace)s"',
|
||||||
issue='no-create-permission',
|
issue='no-create-permission',
|
||||||
namespace=namespace)
|
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,
|
repo = model.create_repository(namespace, repository,
|
||||||
get_authenticated_user())
|
get_authenticated_user())
|
||||||
|
|
||||||
|
|
14
initdb.py
14
initdb.py
|
@ -354,6 +354,10 @@ def populate_database():
|
||||||
reader.verified = True
|
reader.verified = True
|
||||||
reader.save()
|
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 = model.create_user('outsideorg', 'password', 'no2@thanks.com')
|
||||||
outside_org.verified = True
|
outside_org.verified = True
|
||||||
outside_org.save()
|
outside_org.save()
|
||||||
|
@ -490,6 +494,9 @@ def populate_database():
|
||||||
|
|
||||||
model.create_robot('neworgrobot', org)
|
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 = model.get_organization_team('buynlarge', 'owners')
|
||||||
owners.description = 'Owners have unfetterd access across the entire org.'
|
owners.description = 'Owners have unfetterd access across the entire org.'
|
||||||
owners.save()
|
owners.save()
|
||||||
|
@ -504,12 +511,19 @@ def populate_database():
|
||||||
[],
|
[],
|
||||||
(4, [], ['latest', 'prod']))
|
(4, [], ['latest', 'prod']))
|
||||||
|
|
||||||
|
creators = model.create_team('creators', org, 'creator',
|
||||||
|
'Creators of orgrepo.')
|
||||||
|
|
||||||
reader_team = model.create_team('readers', org, 'member',
|
reader_team = model.create_team('readers', org, 'member',
|
||||||
'Readers of orgrepo.')
|
'Readers of orgrepo.')
|
||||||
model.set_team_repo_permission(reader_team.name, org_repo.namespace_user.username, org_repo.name,
|
model.set_team_repo_permission(reader_team.name, org_repo.namespace_user.username, org_repo.name,
|
||||||
'read')
|
'read')
|
||||||
|
|
||||||
model.add_user_to_team(new_user_2, reader_team)
|
model.add_user_to_team(new_user_2, reader_team)
|
||||||
model.add_user_to_team(reader, 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, [],
|
__generate_repository(new_user_1, 'superwide', None, False, [],
|
||||||
[(10, [], 'latest2'),
|
[(10, [], 'latest2'),
|
||||||
|
|
|
@ -346,5 +346,48 @@ class RegistryTests(RegistryTestCase):
|
||||||
# Pull the repository as devtable, which should succeed because the repository is public.
|
# Pull the repository as devtable, which should succeed because the repository is public.
|
||||||
self.do_pull('public', 'newrepo', 'devtable', 'password')
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Reference in a new issue