endpoints.verbs: abort 405 for non-container repos
This commit is contained in:
parent
40b638a981
commit
d5fa2ad0c0
2 changed files with 52 additions and 16 deletions
|
@ -9,6 +9,19 @@ from data import model
|
||||||
from image.docker.v1 import DockerV1Metadata
|
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'])):
|
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.
|
DerivedImage represents a user-facing alias for an image which was derived from another image.
|
||||||
|
@ -43,9 +56,10 @@ class VerbsDataInterface(object):
|
||||||
verbs.
|
verbs.
|
||||||
"""
|
"""
|
||||||
@abstractmethod
|
@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
|
pass
|
||||||
|
|
||||||
|
@ -144,8 +158,8 @@ class PreOCIModel(VerbsDataInterface):
|
||||||
before it was changed to support the OCI specification.
|
before it was changed to support the OCI specification.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def repository_is_public(self, namespace_name, repo_name):
|
def get_repository(self, namespace_name, repo_name):
|
||||||
return model.repository.repository_is_public(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):
|
def get_manifest_layers_with_blobs(self, repo_image):
|
||||||
repo_image_record = model.image.get_image_by_id(repo_image.repository.namespace_name,
|
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,
|
uploading=blob_record.uploading,
|
||||||
locations=locations,
|
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),
|
||||||
|
)
|
||||||
|
|
|
@ -154,29 +154,35 @@ def _torrent_repo_verb(repo_image, tag, verb, **kwargs):
|
||||||
abort(406)
|
abort(406)
|
||||||
|
|
||||||
# Return the torrent.
|
# Return the torrent.
|
||||||
public_repo = model.repository_is_public(repo_image.repository.namespace_name,
|
repo = model.get_repository(repo_image.repository.namespace_name,
|
||||||
repo_image.repository.name)
|
repo_image.repository.name)
|
||||||
torrent = _torrent_for_blob(derived_image.blob, public_repo)
|
repo_is_public = repo is not None and repo.is_public
|
||||||
|
torrent = _torrent_for_blob(derived_image.blob, repo_is_public)
|
||||||
|
|
||||||
# Log the action.
|
# Log the action.
|
||||||
track_and_log('repo_verb', repo_image.repository, tag=tag, verb=verb, torrent=True, **kwargs)
|
track_and_log('repo_verb', repo_image.repository, tag=tag, verb=verb, torrent=True, **kwargs)
|
||||||
return torrent
|
return torrent
|
||||||
|
|
||||||
|
|
||||||
def _verify_repo_verb(_, namespace, repository, tag, verb, checker=None):
|
def _verify_repo_verb(_, namespace, repo_name, tag, verb, checker=None):
|
||||||
permission = ReadRepositoryPermission(namespace, repository)
|
permission = ReadRepositoryPermission(namespace, repo_name)
|
||||||
if not permission.can() and not model.repository_is_public(namespace, repository):
|
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)
|
abort(403)
|
||||||
|
|
||||||
# Lookup the requested tag.
|
# 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:
|
if tag_image is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
if repo.kind != 'image':
|
||||||
|
abort(405)
|
||||||
|
|
||||||
# If there is a data checker, call it first.
|
# If there is a data checker, call it first.
|
||||||
if checker is not None:
|
if checker is not None:
|
||||||
if not checker(tag_image):
|
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)
|
abort(404)
|
||||||
|
|
||||||
return tag_image
|
return tag_image
|
||||||
|
@ -346,18 +352,23 @@ def get_squashed_tag(namespace, repository, tag):
|
||||||
@parse_repository_name()
|
@parse_repository_name()
|
||||||
def get_tag_torrent(namespace_name, repo_name, digest):
|
def get_tag_torrent(namespace_name, repo_name, digest):
|
||||||
permission = ReadRepositoryPermission(namespace_name, repo_name)
|
permission = ReadRepositoryPermission(namespace_name, repo_name)
|
||||||
public_repo = model.repository_is_public(namespace_name, repo_name)
|
repo = model.get_repository(namespace_name, repo_name)
|
||||||
if not permission.can() and not public_repo:
|
repo_is_public = repo is not None and repo.is_public
|
||||||
|
|
||||||
|
if not permission.can() and not repo_is_public:
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
user = get_authenticated_user()
|
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)
|
# We can not generate a private torrent cluster without a user uuid (e.g. token auth)
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
if repo.kind != 'image':
|
||||||
|
abort(405)
|
||||||
|
|
||||||
blob = model.get_repo_blob_by_digest(namespace_name, repo_name, digest)
|
blob = model.get_repo_blob_by_digest(namespace_name, repo_name, digest)
|
||||||
if blob is None:
|
if blob is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
metric_queue.repository_pull.Inc(labelvalues=[namespace_name, repo_name, 'torrent', True])
|
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)
|
||||||
|
|
Reference in a new issue