diff --git a/data/interfaces/verbs.py b/data/interfaces/verbs.py index f5758352e..5f1e02c70 100644 --- a/data/interfaces/verbs.py +++ b/data/interfaces/verbs.py @@ -9,6 +9,19 @@ from data import model from image.docker.v1 import DockerV1Metadata +class Repository(namedtuple('Repository', ['id', 'name', 'namespace_name', 'description', + 'is_public', 'kind'])): + """ + Repository represents a namespaced collection of tags. + :type id: int + :type name: string + :type namespace_name: string + :type description: string + :type is_public: bool + :type kind: string + """ + + class DerivedImage(namedtuple('DerivedImage', ['ref', 'blob', 'internal_source_image_db_id'])): """ DerivedImage represents a user-facing alias for an image which was derived from another image. @@ -43,9 +56,10 @@ class VerbsDataInterface(object): verbs. """ @abstractmethod - def repository_is_public(self, namespace_name, repo_name): + def get_repository(self, namespace_name, repo_name): """ - Returns a boolean for whether the repository with the given name and namespace is public. + Returns a repository tuple for the repository with the given name under the given namespace. + Returns None if no such repository was found. """ pass @@ -144,8 +158,8 @@ class PreOCIModel(VerbsDataInterface): before it was changed to support the OCI specification. """ - def repository_is_public(self, namespace_name, repo_name): - return model.repository.repository_is_public(namespace_name, repo_name) + def get_repository(self, namespace_name, repo_name): + return _repository_for_repo(model.repository.get_repository(namespace_name, repo_name)) def get_manifest_layers_with_blobs(self, repo_image): repo_image_record = model.image.get_image_by_id(repo_image.repository.namespace_name, @@ -320,3 +334,14 @@ def _blob(blob_record): uploading=blob_record.uploading, locations=locations, ) + +def _repository_for_repo(repo): + """ Returns a Repository object representing the Pre-OCI data model repo instance given. """ + return Repository( + id=repo.id, + name=repo.name, + namespace_name=repo.namespace_user.username, + description=repo.description, + is_public=model.repository.is_repository_public(repo), + kind=model.repository.get_repo_kind_name(repo), + ) diff --git a/endpoints/verbs/__init__.py b/endpoints/verbs/__init__.py index ff2c28f76..16453ed08 100644 --- a/endpoints/verbs/__init__.py +++ b/endpoints/verbs/__init__.py @@ -154,29 +154,35 @@ def _torrent_repo_verb(repo_image, tag, verb, **kwargs): abort(406) # Return the torrent. - public_repo = model.repository_is_public(repo_image.repository.namespace_name, - repo_image.repository.name) - torrent = _torrent_for_blob(derived_image.blob, public_repo) + repo = model.get_repository(repo_image.repository.namespace_name, + repo_image.repository.name) + repo_is_public = repo is not None and repo.is_public + torrent = _torrent_for_blob(derived_image.blob, repo_is_public) # Log the action. track_and_log('repo_verb', repo_image.repository, tag=tag, verb=verb, torrent=True, **kwargs) return torrent -def _verify_repo_verb(_, namespace, repository, tag, verb, checker=None): - permission = ReadRepositoryPermission(namespace, repository) - if not permission.can() and not model.repository_is_public(namespace, repository): +def _verify_repo_verb(_, namespace, repo_name, tag, verb, checker=None): + permission = ReadRepositoryPermission(namespace, repo_name) + repo = model.get_repository(namespace, repo_name) + repo_is_public = repo is not None and repo.is_public + if not permission.can() and not repo_is_public: abort(403) # Lookup the requested tag. - tag_image = model.get_tag_image(namespace, repository, tag) + tag_image = model.get_tag_image(namespace, repo_name, tag) if tag_image is None: abort(404) + if repo.kind != 'image': + abort(405) + # If there is a data checker, call it first. if checker is not None: if not checker(tag_image): - logger.debug('Check mismatch on %s/%s:%s, verb %s', namespace, repository, tag, verb) + logger.debug('Check mismatch on %s/%s:%s, verb %s', namespace, repo_name, tag, verb) abort(404) return tag_image @@ -346,18 +352,23 @@ def get_squashed_tag(namespace, repository, tag): @parse_repository_name() def get_tag_torrent(namespace_name, repo_name, digest): permission = ReadRepositoryPermission(namespace_name, repo_name) - public_repo = model.repository_is_public(namespace_name, repo_name) - if not permission.can() and not public_repo: + repo = model.get_repository(namespace_name, repo_name) + repo_is_public = repo is not None and repo.is_public + + if not permission.can() and not repo_is_public: abort(403) user = get_authenticated_user() - if user is None and not public_repo: + if user is None and not repo_is_public: # We can not generate a private torrent cluster without a user uuid (e.g. token auth) abort(403) + if repo.kind != 'image': + abort(405) + blob = model.get_repo_blob_by_digest(namespace_name, repo_name, digest) if blob is None: abort(404) metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'torrent', True]) - return _torrent_for_blob(blob, public_repo) + return _torrent_for_blob(blob, repo_is_public)