From e4ffaff869c1295318171f3fe03f140c06266e0a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Thu, 21 Jan 2016 15:40:51 -0500 Subject: [PATCH] Fix Docker Auth and our V2 registry paths to support library (i.e. namespace-less) repositories. This support is placed behind a feature flag. --- app.py | 44 +++++++++++--- auth/registry_jwt_auth.py | 3 +- config.py | 10 ++++ endpoints/api/__init__.py | 2 +- endpoints/api/build.py | 8 +-- endpoints/api/image.py | 4 +- endpoints/api/logs.py | 4 +- endpoints/api/permission.py | 10 ++-- endpoints/api/repoemail.py | 2 +- endpoints/api/repository.py | 4 +- endpoints/api/repositorynotification.py | 6 +- endpoints/api/repotoken.py | 4 +- endpoints/api/secscan.py | 4 +- endpoints/api/tag.py | 8 +-- endpoints/api/trigger.py | 18 +++--- endpoints/api/user.py | 2 +- endpoints/bitbuckettrigger.py | 1 - endpoints/common.py | 22 +++++-- endpoints/githubtrigger.py | 5 +- endpoints/oauthlogin.py | 3 +- endpoints/v1/index.py | 13 ++-- endpoints/v1/tag.py | 11 ++-- endpoints/v2/blob.py | 34 +++++++---- endpoints/v2/manifest.py | 59 ++++++++++++++---- endpoints/v2/tag.py | 7 ++- endpoints/v2/v2auth.py | 7 ++- endpoints/verbs.py | 3 +- endpoints/web.py | 12 ++-- endpoints/webhooks.py | 2 +- initdb.py | 3 + test/data/test.db | Bin 1142784 -> 1142784 bytes test/registry_tests.py | 76 +++++++++++++++++------- test/specs.py | 3 +- test/test_api_usage.py | 2 +- test/test_v1_endpoint_security.py | 2 +- test/test_v2_endpoint_security.py | 1 - util/names.py | 19 +----- 37 files changed, 270 insertions(+), 148 deletions(-) diff --git a/app.py b/app.py index 7d551189c..006084bda 100644 --- a/app.py +++ b/app.py @@ -50,15 +50,6 @@ DOCKER_V2_SIGNINGKEY_FILENAME = 'docker_v2.pem' app = Flask(__name__) logger = logging.getLogger(__name__) - -class RegexConverter(BaseConverter): - def __init__(self, url_map, *items): - super(RegexConverter, self).__init__(url_map) - self.regex = items[0] - - -app.url_map.converters['regex'] = RegexConverter - # Instantiate the configuration. is_testing = 'TEST' in os.environ is_kubernetes = 'KUBERNETES_SERVICE_HOST' in os.environ @@ -126,6 +117,41 @@ if app.config['SECRET_KEY'] is None: features.import_features(app.config) +# Register custom converters. +class RegexConverter(BaseConverter): + """ Converter for handling custom regular expression patterns in paths. """ + def __init__(self, url_map, *items): + super(RegexConverter, self).__init__(url_map) + self.regex = items[0] + +class RepositoryPathConverter(BaseConverter): + """ Converter for handling repository paths. Handles both library and non-library paths (if + configured). + """ + def __init__(self, url_map, *items): + super(RepositoryPathConverter, self).__init__(url_map) + self.weight = 200 + + if features.LIBRARY_SUPPORT: + # Allow names without namespaces. + self.regex = r'[^/]+(/[^/]+)?' + else: + self.regex = r'([^/]+/[^/]+)' + + +class APIRepositoryPathConverter(BaseConverter): + """ Converter for handling repository paths. Does not handle library paths. + """ + def __init__(self, url_map, *items): + super(APIRepositoryPathConverter, self).__init__(url_map) + self.weight = 200 + self.regex = r'([^/]+/[^/]+)' + + +app.url_map.converters['regex'] = RegexConverter +app.url_map.converters['repopath'] = RepositoryPathConverter +app.url_map.converters['apirepopath'] = APIRepositoryPathConverter + Principal(app, use_sessions=False) avatar = Avatar(app) diff --git a/auth/registry_jwt_auth.py b/auth/registry_jwt_auth.py index 9145dc1c0..42ca5329b 100644 --- a/auth/registry_jwt_auth.py +++ b/auth/registry_jwt_auth.py @@ -207,8 +207,9 @@ def identity_from_bearer_token(bearer_token, max_signed_s, public_key): logger.exception('We should not be minting invalid credentials') raise InvalidJWTException('Token contained invalid or malformed access grants') + lib_namespace = app.config['LIBRARY_NAMESPACE'] for grant in payload['access']: - namespace, repo_name = parse_namespace_repository(grant['name']) + namespace, repo_name = parse_namespace_repository(grant['name'], lib_namespace) if 'push' in grant['actions']: loaded_identity.provides.add(repository_write_grant(namespace, repo_name)) diff --git a/config.py b/config.py index 49ecdd50e..ac0d6ff95 100644 --- a/config.py +++ b/config.py @@ -199,6 +199,16 @@ class DefaultConfig(object): # Feature Flag: Whether or not to rotate old action logs to storage. FEATURE_ACTION_LOG_ROTATION = False + # Feature Flag: Whether to allow for "namespace-less" repositories when pulling and pushing from + # Docker. + FEATURE_LIBRARY_SUPPORT = True + + # The namespace to use for library repositories. + # Note: This must remain 'library' until Docker removes their hard-coded namespace for libraries. + # See: https://github.com/docker/docker/blob/master/registry/session.go#L320 + LIBRARY_NAMESPACE = 'library' + + BUILD_MANAGER = ('enterprise', {}) DISTRIBUTED_STORAGE_CONFIG = { diff --git a/endpoints/api/__init__.py b/endpoints/api/__init__.py index a83a7ac58..018b5277d 100644 --- a/endpoints/api/__init__.py +++ b/endpoints/api/__init__.py @@ -227,7 +227,7 @@ def parse_args(func): def parse_repository_name(func): @wraps(func) def wrapper(repository, *args, **kwargs): - (namespace, repository) = parse_namespace_repository(repository) + (namespace, repository) = parse_namespace_repository(repository, app.config['LIBRARY_NAMESPACE']) return func(namespace, repository, *args, **kwargs) return wrapper diff --git a/endpoints/api/build.py b/endpoints/api/build.py index c517e1f35..97857638f 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -145,7 +145,7 @@ def build_status_view(build_obj): return resp -@resource('/v1/repository//build/') +@resource('/v1/repository//build/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryBuildList(RepositoryParamResource): """ Resource related to creating and listing repository builds. """ @@ -288,7 +288,7 @@ class RepositoryBuildList(RepositoryParamResource): -@resource('/v1/repository//build/') +@resource('/v1/repository//build/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('build_uuid', 'The UUID of the build') class RepositoryBuildResource(RepositoryParamResource): @@ -322,7 +322,7 @@ class RepositoryBuildResource(RepositoryParamResource): raise InvalidRequest('Build is currently running or has finished') -@resource('/v1/repository//build//status') +@resource('/v1/repository//build//status') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('build_uuid', 'The UUID of the build') class RepositoryBuildStatus(RepositoryParamResource): @@ -339,7 +339,7 @@ class RepositoryBuildStatus(RepositoryParamResource): return build_status_view(build) -@resource('/v1/repository//build//logs') +@resource('/v1/repository//build//logs') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('build_uuid', 'The UUID of the build') class RepositoryBuildLogs(RepositoryParamResource): diff --git a/endpoints/api/image.py b/endpoints/api/image.py index 55c8da0a2..53e38409d 100644 --- a/endpoints/api/image.py +++ b/endpoints/api/image.py @@ -43,7 +43,7 @@ def historical_image_view(image, image_map): return normal_view -@resource('/v1/repository//image/') +@resource('/v1/repository//image/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryImageList(RepositoryParamResource): """ Resource for listing repository images. """ @@ -82,7 +82,7 @@ class RepositoryImageList(RepositoryParamResource): } -@resource('/v1/repository//image/') +@resource('/v1/repository//image/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('image_id', 'The Docker image ID') class RepositoryImage(RepositoryParamResource): diff --git a/endpoints/api/logs.py b/endpoints/api/logs.py index 4610dfb91..b3e910401 100644 --- a/endpoints/api/logs.py +++ b/endpoints/api/logs.py @@ -106,7 +106,7 @@ def get_aggregate_logs(start_time, end_time, performer_name=None, repository=Non } -@resource('/v1/repository//logs') +@resource('/v1/repository//logs') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryLogs(RepositoryParamResource): """ Resource for fetching logs for the specific repository. """ @@ -175,7 +175,7 @@ class OrgLogs(ApiResource): raise Unauthorized() -@resource('/v1/repository//aggregatelogs') +@resource('/v1/repository//aggregatelogs') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryAggregateLogs(RepositoryParamResource): """ Resource for fetching aggregated logs for the specific repository. """ diff --git a/endpoints/api/permission.py b/endpoints/api/permission.py index 81150abb2..3039346ea 100644 --- a/endpoints/api/permission.py +++ b/endpoints/api/permission.py @@ -38,7 +38,7 @@ def wrap_role_view_team(role_json, team): return role_json -@resource('/v1/repository//permissions/team/') +@resource('/v1/repository//permissions/team/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryTeamPermissionList(RepositoryParamResource): """ Resource for repository team permissions. """ @@ -57,7 +57,7 @@ class RepositoryTeamPermissionList(RepositoryParamResource): } -@resource('/v1/repository//permissions/user/') +@resource('/v1/repository//permissions/user/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryUserPermissionList(RepositoryParamResource): """ Resource for repository user permissions. """ @@ -97,7 +97,7 @@ class RepositoryUserPermissionList(RepositoryParamResource): } -@resource('/v1/repository//permissions/user//transitive') +@resource('/v1/repository//permissions/user//transitive') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('username', 'The username of the user to which the permissions apply') class RepositoryUserTransitivePermission(RepositoryParamResource): @@ -121,7 +121,7 @@ class RepositoryUserTransitivePermission(RepositoryParamResource): } -@resource('/v1/repository//permissions/user/') +@resource('/v1/repository//permissions/user/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('username', 'The username of the user to which the permission applies') class RepositoryUserPermission(RepositoryParamResource): @@ -215,7 +215,7 @@ class RepositoryUserPermission(RepositoryParamResource): return 'Deleted', 204 -@resource('/v1/repository//permissions/team/') +@resource('/v1/repository//permissions/team/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('teamname', 'The name of the team to which the permission applies') class RepositoryTeamPermission(RepositoryParamResource): diff --git a/endpoints/api/repoemail.py b/endpoints/api/repoemail.py index 8f01480fa..ce3d61294 100644 --- a/endpoints/api/repoemail.py +++ b/endpoints/api/repoemail.py @@ -30,7 +30,7 @@ def record_view(record): @internal_only @show_if(features.MAILING) -@resource('/v1/repository//authorizedemail/') +@resource('/v1/repository//authorizedemail/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('email', 'The e-mail address') class RepositoryAuthorizedEmail(RepositoryParamResource): diff --git a/endpoints/api/repository.py b/endpoints/api/repository.py index d89475dca..18f4fc432 100644 --- a/endpoints/api/repository.py +++ b/endpoints/api/repository.py @@ -225,7 +225,7 @@ class RepositoryList(ApiResource): } -@resource('/v1/repository/') +@resource('/v1/repository/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class Repository(RepositoryParamResource): """Operations for managing a specific repository.""" @@ -339,7 +339,7 @@ class Repository(RepositoryParamResource): return 'Deleted', 204 -@resource('/v1/repository//changevisibility') +@resource('/v1/repository//changevisibility') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryVisibility(RepositoryParamResource): """ Custom verb for changing the visibility of the repository. """ diff --git a/endpoints/api/repositorynotification.py b/endpoints/api/repositorynotification.py index f1b9e013a..2441ca451 100644 --- a/endpoints/api/repositorynotification.py +++ b/endpoints/api/repositorynotification.py @@ -38,7 +38,7 @@ def notification_view(note): } -@resource('/v1/repository//notification/') +@resource('/v1/repository//notification/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryNotificationList(RepositoryParamResource): """ Resource for dealing with listing and creating notifications on a repository. """ @@ -116,7 +116,7 @@ class RepositoryNotificationList(RepositoryParamResource): } -@resource('/v1/repository//notification/') +@resource('/v1/repository//notification/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('uuid', 'The UUID of the notification') class RepositoryNotification(RepositoryParamResource): @@ -149,7 +149,7 @@ class RepositoryNotification(RepositoryParamResource): return 'No Content', 204 -@resource('/v1/repository//notification//test') +@resource('/v1/repository//notification//test') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('uuid', 'The UUID of the notification') class TestRepositoryNotification(RepositoryParamResource): diff --git a/endpoints/api/repotoken.py b/endpoints/api/repotoken.py index 46208c5ec..57aefc9a5 100644 --- a/endpoints/api/repotoken.py +++ b/endpoints/api/repotoken.py @@ -20,7 +20,7 @@ def token_view(token_obj): } -@resource('/v1/repository//tokens/') +@resource('/v1/repository//tokens/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class RepositoryTokenList(RepositoryParamResource): """ Resource for creating and listing repository tokens. """ @@ -66,7 +66,7 @@ class RepositoryTokenList(RepositoryParamResource): return token_view(token), 201 -@resource('/v1/repository//tokens/') +@resource('/v1/repository//tokens/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('code', 'The token code') class RepositoryToken(RepositoryParamResource): diff --git a/endpoints/api/secscan.py b/endpoints/api/secscan.py index b4b3681fc..272437bc4 100644 --- a/endpoints/api/secscan.py +++ b/endpoints/api/secscan.py @@ -54,7 +54,7 @@ def _get_status(repo_image): @show_if(features.SECURITY_SCANNER) -@resource('/v1/repository//image//vulnerabilities') +@resource('/v1/repository//image//vulnerabilities') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('imageid', 'The image ID') class RepositoryImageVulnerabilities(RepositoryParamResource): @@ -89,7 +89,7 @@ class RepositoryImageVulnerabilities(RepositoryParamResource): @show_if(features.SECURITY_SCANNER) -@resource('/v1/repository//image//packages') +@resource('/v1/repository//image//packages') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('imageid', 'The image ID') class RepositoryImagePackages(RepositoryParamResource): diff --git a/endpoints/api/tag.py b/endpoints/api/tag.py index 416d3e3f3..2de6b90ee 100644 --- a/endpoints/api/tag.py +++ b/endpoints/api/tag.py @@ -11,7 +11,7 @@ from auth.auth_context import get_authenticated_user from util.names import TAG_ERROR, TAG_REGEX -@resource('/v1/repository//tag/') +@resource('/v1/repository//tag/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class ListRepositoryTags(RepositoryParamResource): """ Resource for listing full repository tag history, alive *and dead*. """ @@ -60,7 +60,7 @@ class ListRepositoryTags(RepositoryParamResource): } -@resource('/v1/repository//tag/') +@resource('/v1/repository//tag/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('tag', 'The name of the tag') class RepositoryTag(RepositoryParamResource): @@ -128,7 +128,7 @@ class RepositoryTag(RepositoryParamResource): return 'Deleted', 204 -@resource('/v1/repository//tag//images') +@resource('/v1/repository//tag//images') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('tag', 'The name of the tag') class RepositoryTagImages(RepositoryParamResource): @@ -179,7 +179,7 @@ class RepositoryTagImages(RepositoryParamResource): -@resource('/v1/repository//tag//revert') +@resource('/v1/repository//tag//revert') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('tag', 'The name of the tag') class RevertTag(RepositoryParamResource): diff --git a/endpoints/api/trigger.py b/endpoints/api/trigger.py index b1f37f727..66ca30a76 100644 --- a/endpoints/api/trigger.py +++ b/endpoints/api/trigger.py @@ -33,7 +33,7 @@ def _prepare_webhook_url(scheme, username, password, hostname, path): return urlunparse((scheme, auth_hostname, path, '', '', '')) -@resource('/v1/repository//trigger/') +@resource('/v1/repository//trigger/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class BuildTriggerList(RepositoryParamResource): """ Resource for listing repository build triggers. """ @@ -48,7 +48,7 @@ class BuildTriggerList(RepositoryParamResource): } -@resource('/v1/repository//trigger/') +@resource('/v1/repository//trigger/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') class BuildTrigger(RepositoryParamResource): @@ -95,7 +95,7 @@ class BuildTrigger(RepositoryParamResource): return 'No Content', 204 -@resource('/v1/repository//trigger//subdir') +@resource('/v1/repository//trigger//subdir') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') @internal_only @@ -143,7 +143,7 @@ class BuildTriggerSubdirs(RepositoryParamResource): raise Unauthorized() -@resource('/v1/repository//trigger//activate') +@resource('/v1/repository//trigger//activate') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') class BuildTriggerActivate(RepositoryParamResource): @@ -245,7 +245,7 @@ class BuildTriggerActivate(RepositoryParamResource): raise Unauthorized() -@resource('/v1/repository//trigger//analyze') +@resource('/v1/repository//trigger//analyze') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') @internal_only @@ -384,7 +384,7 @@ class BuildTriggerAnalyze(RepositoryParamResource): raise NotFound() -@resource('/v1/repository//trigger//start') +@resource('/v1/repository//trigger//start') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') class ActivateBuildTrigger(RepositoryParamResource): @@ -444,7 +444,7 @@ class ActivateBuildTrigger(RepositoryParamResource): return resp, 201, headers -@resource('/v1/repository//trigger//builds') +@resource('/v1/repository//trigger//builds') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') class TriggerBuildList(RepositoryParamResource): @@ -464,7 +464,7 @@ class TriggerBuildList(RepositoryParamResource): FIELD_VALUE_LIMIT = 30 -@resource('/v1/repository//trigger//fields/') +@resource('/v1/repository//trigger//fields/') @internal_only class BuildTriggerFieldValues(RepositoryParamResource): """ Custom verb to fetch a values list for a particular field name. """ @@ -493,7 +493,7 @@ class BuildTriggerFieldValues(RepositoryParamResource): raise Unauthorized() -@resource('/v1/repository//trigger//sources') +@resource('/v1/repository//trigger//sources') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') @internal_only diff --git a/endpoints/api/user.py b/endpoints/api/user.py index b97040c32..0c4868b39 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -876,7 +876,7 @@ class StarredRepositoryList(ApiResource): }, 201 -@resource('/v1/user/starred/') +@resource('/v1/user/starred/') @path_param('repository', 'The full path of the repository. e.g. namespace/name') class StarredRepository(RepositoryParamResource): """ Operations for managing a specific starred repository. """ diff --git a/endpoints/bitbuckettrigger.py b/endpoints/bitbuckettrigger.py index 7045200f9..b3a9ca63a 100644 --- a/endpoints/bitbuckettrigger.py +++ b/endpoints/bitbuckettrigger.py @@ -8,7 +8,6 @@ from buildtrigger.bitbuckethandler import BitbucketBuildTrigger from endpoints.common import route_show_if from app import app from data import model -from util.names import parse_repository_name from util.http import abort from auth.auth import require_session_login diff --git a/endpoints/common.py b/endpoints/common.py index 371c66384..5d5a8258e 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -23,6 +23,7 @@ from functools import wraps from config import frontend_visible_config from external_libraries import get_external_javascript, get_external_css from util.secscan.api import PRIORITY_LEVELS +from util.names import parse_namespace_repository import features @@ -47,11 +48,24 @@ def get_cache_busters(): return CACHE_BUSTERS -class RepoPathConverter(BaseConverter): - regex = '[\.a-zA-Z0-9_\-]+/[\.a-zA-Z0-9_\-]+' - weight = 200 +def parse_repository_name(f): + @wraps(f) + def wrapper(repository, *args, **kwargs): + lib_namespace = app.config['LIBRARY_NAMESPACE'] + (namespace, repository) = parse_namespace_repository(repository, lib_namespace) + return f(namespace, repository, *args, **kwargs) + return wrapper + + +def parse_repository_name_and_tag(f): + @wraps(f) + def wrapper(repository, *args, **kwargs): + lib_namespace = app.config['LIBRARY_NAMESPACE'] + namespace, repository, tag = parse_namespace_repository(repository, lib_namespace, + include_tag=True) + return f(namespace, repository, tag, *args, **kwargs) + return wrapper -app.url_map.converters['repopath'] = RepoPathConverter def route_show_if(value): def decorator(f): diff --git a/endpoints/githubtrigger.py b/endpoints/githubtrigger.py index 40b00ae04..5e2553d22 100644 --- a/endpoints/githubtrigger.py +++ b/endpoints/githubtrigger.py @@ -3,10 +3,9 @@ import logging from flask import request, redirect, url_for, Blueprint from flask.ext.login import current_user -from endpoints.common import route_show_if +from endpoints.common import route_show_if, parse_repository_name from app import app, github_trigger from data import model -from util.names import parse_repository_name from util.http import abort from auth.permissions import AdministerRepositoryPermission from auth.auth import require_session_login @@ -17,7 +16,7 @@ logger = logging.getLogger(__name__) client = app.config['HTTPCLIENT'] githubtrigger = Blueprint('callback', __name__) -@githubtrigger.route('/github/callback/trigger/', methods=['GET']) +@githubtrigger.route('/github/callback/trigger/', methods=['GET']) @route_show_if(features.GITHUB_BUILD) @require_session_login @parse_repository_name diff --git a/endpoints/oauthlogin.py b/endpoints/oauthlogin.py index 3ca75574b..9c99836b6 100644 --- a/endpoints/oauthlogin.py +++ b/endpoints/oauthlogin.py @@ -4,11 +4,10 @@ import requests from flask import request, redirect, url_for, Blueprint from flask.ext.login import current_user -from endpoints.common import common_login, route_show_if +from endpoints.common import common_login, route_show_if, parse_repository_name from endpoints.web import render_page_template_with_routedata from app import app, analytics, get_app_url, github_login, google_login, dex_login from data import model -from util.names import parse_repository_name from util.validation import generate_valid_usernames from util.http import abort from auth.auth import require_session_login diff --git a/endpoints/v1/index.py b/endpoints/v1/index.py index 1d4ad2365..13a2c4ed9 100644 --- a/endpoints/v1/index.py +++ b/endpoints/v1/index.py @@ -9,12 +9,13 @@ from data import model from app import app, authentication, userevents, storage from auth.auth import process_auth, generate_signed_token from auth.auth_context import get_authenticated_user, get_validated_token, get_validated_oauth_token -from util.names import parse_repository_name, REPOSITORY_NAME_REGEX +from util.names import REPOSITORY_NAME_REGEX from auth.permissions import (ModifyRepositoryPermission, UserAdminPermission, ReadRepositoryPermission, CreateRepositoryPermission, repository_read_grant, repository_write_grant) from util.http import abort +from endpoints.common import parse_repository_name from endpoints.v1 import v1_bp from endpoints.trackhelper import track_and_log from endpoints.notificationhelper import spawn_notification @@ -167,7 +168,7 @@ def update_user(username): abort(403) -@v1_bp.route('/repositories/', methods=['PUT']) +@v1_bp.route('/repositories/', methods=['PUT']) @process_auth @parse_repository_name @generate_headers(scope=GrantType.WRITE_REPOSITORY, add_grant_for_status=201) @@ -221,7 +222,7 @@ def create_repository(namespace, repository): return make_response('Created', 201) -@v1_bp.route('/repositories//images', methods=['PUT']) +@v1_bp.route('/repositories//images', methods=['PUT']) @process_auth @parse_repository_name @generate_headers(scope=GrantType.WRITE_REPOSITORY) @@ -251,7 +252,7 @@ def update_images(namespace, repository): abort(403) -@v1_bp.route('/repositories//images', methods=['GET']) +@v1_bp.route('/repositories//images', methods=['GET']) @process_auth @parse_repository_name @generate_headers(scope=GrantType.READ_REPOSITORY) @@ -277,7 +278,7 @@ def get_repository_images(namespace, repository): abort(403) -@v1_bp.route('/repositories//images', methods=['DELETE']) +@v1_bp.route('/repositories//images', methods=['DELETE']) @process_auth @parse_repository_name @generate_headers(scope=GrantType.WRITE_REPOSITORY) @@ -286,7 +287,7 @@ def delete_repository_images(namespace, repository): abort(501, 'Not Implemented', issue='not-implemented') -@v1_bp.route('/repositories//auth', methods=['PUT']) +@v1_bp.route('/repositories//auth', methods=['PUT']) @parse_repository_name @anon_allowed def put_repository_auth(namespace, repository): diff --git a/endpoints/v1/tag.py b/endpoints/v1/tag.py index 644431d2f..d9fd509e5 100644 --- a/endpoints/v1/tag.py +++ b/endpoints/v1/tag.py @@ -5,11 +5,12 @@ import json from flask import abort, request, jsonify, make_response, session from app import app -from util.names import TAG_ERROR, TAG_REGEX, parse_repository_name +from util.names import TAG_ERROR, TAG_REGEX from auth.auth import process_auth from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission) from data import model +from endpoints.common import parse_repository_name from endpoints.decorators import anon_protect from endpoints.v1 import v1_bp from endpoints.trackhelper import track_and_log @@ -18,7 +19,7 @@ from endpoints.trackhelper import track_and_log logger = logging.getLogger(__name__) -@v1_bp.route('/repositories//tags', methods=['GET']) +@v1_bp.route('/repositories//tags', methods=['GET']) @process_auth @anon_protect @parse_repository_name @@ -33,7 +34,7 @@ def get_tags(namespace, repository): abort(403) -@v1_bp.route('/repositories//tags/', methods=['GET']) +@v1_bp.route('/repositories//tags/', methods=['GET']) @process_auth @anon_protect @parse_repository_name @@ -53,7 +54,7 @@ def get_tag(namespace, repository, tag): abort(403) -@v1_bp.route('/repositories//tags/', methods=['PUT']) +@v1_bp.route('/repositories//tags/', methods=['PUT']) @process_auth @anon_protect @parse_repository_name @@ -78,7 +79,7 @@ def put_tag(namespace, repository, tag): abort(403) -@v1_bp.route('/repositories//tags/', methods=['DELETE']) +@v1_bp.route('/repositories//tags/', methods=['DELETE']) @process_auth @anon_protect @parse_repository_name diff --git a/endpoints/v2/blob.py b/endpoints/v2/blob.py index 745adf79a..9883ee74a 100644 --- a/endpoints/v2/blob.py +++ b/endpoints/v2/blob.py @@ -17,13 +17,14 @@ from util.cache import cache_control from util.registry.filelike import wrap_with_handler, StreamSlice from util.registry.gzipstream import calculate_size_handler from util.registry.torrent import PieceHasher +from endpoints.common import parse_repository_name from storage.basestorage import InvalidChunkException logger = logging.getLogger(__name__) -BASE_BLOB_ROUTE = '///blobs/' +BASE_BLOB_ROUTE = '//blobs/' BLOB_DIGEST_ROUTE = BASE_BLOB_ROUTE.format(digest_tools.DIGEST_PATTERN) RANGE_HEADER_REGEX = re.compile(r'^bytes=([0-9]+)-([0-9]+)$') BLOB_CONTENT_TYPE = 'application/octet-stream' @@ -57,6 +58,7 @@ def _base_blob_fetch(namespace, repo_name, digest): @v2_bp.route(BLOB_DIGEST_ROUTE, methods=['HEAD']) @process_registry_jwt_auth +@parse_repository_name @require_repo_read @anon_protect @cache_control(max_age=31436000) @@ -72,6 +74,7 @@ def check_blob_exists(namespace, repo_name, digest): @v2_bp.route(BLOB_DIGEST_ROUTE, methods=['GET']) @process_registry_jwt_auth +@parse_repository_name @require_repo_read @anon_protect @cache_control(max_age=31536000) @@ -103,8 +106,9 @@ def _render_range(num_uploaded_bytes, with_bytes_prefix=True): return '{0}0-{1}'.format('bytes=' if with_bytes_prefix else '', num_uploaded_bytes - 1) -@v2_bp.route('///blobs/uploads/', methods=['POST']) +@v2_bp.route('//blobs/uploads/', methods=['POST']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def start_blob_upload(namespace, repo_name): @@ -121,8 +125,10 @@ def start_blob_upload(namespace, repo_name): if digest is None: # The user will send the blob data in another request accepted = make_response('', 202) - accepted.headers['Location'] = url_for('v2.upload_chunk', namespace=namespace, - repo_name=repo_name, upload_uuid=new_upload_uuid) + accepted.headers['Location'] = url_for('v2.upload_chunk', + repository='%s/%s' % (namespace, repo_name), + upload_uuid=new_upload_uuid) + accepted.headers['Range'] = _render_range(0) accepted.headers['Docker-Upload-UUID'] = new_upload_uuid return accepted @@ -136,8 +142,9 @@ def start_blob_upload(namespace, repo_name): return _finish_upload(namespace, repo_name, uploaded, digest) -@v2_bp.route('///blobs/uploads/', methods=['GET']) +@v2_bp.route('//blobs/uploads/', methods=['GET']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def fetch_existing_upload(namespace, repo_name, upload_uuid): @@ -311,13 +318,15 @@ def _finish_upload(namespace, repo_name, upload_obj, expected_digest): response = make_response('', 201) response.headers['Docker-Content-Digest'] = expected_digest - response.headers['Location'] = url_for('v2.download_blob', namespace=namespace, - repo_name=repo_name, digest=expected_digest) + response.headers['Location'] = url_for('v2.download_blob', + repository='%s/%s' % (namespace, repo_name), + digest=expected_digest) return response -@v2_bp.route('///blobs/uploads/', methods=['PATCH']) +@v2_bp.route('//blobs/uploads/', methods=['PATCH']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def upload_chunk(namespace, repo_name, upload_uuid): @@ -334,8 +343,9 @@ def upload_chunk(namespace, repo_name, upload_uuid): return accepted -@v2_bp.route('///blobs/uploads/', methods=['PUT']) +@v2_bp.route('//blobs/uploads/', methods=['PUT']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def monolithic_upload_or_last_chunk(namespace, repo_name, upload_uuid): @@ -352,8 +362,9 @@ def monolithic_upload_or_last_chunk(namespace, repo_name, upload_uuid): return _finish_upload(namespace, repo_name, found, digest) -@v2_bp.route('///blobs/uploads/', methods=['DELETE']) +@v2_bp.route('//blobs/uploads/', methods=['DELETE']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def cancel_upload(namespace, repo_name, upload_uuid): @@ -371,8 +382,9 @@ def cancel_upload(namespace, repo_name, upload_uuid): -@v2_bp.route('///blobs/', methods=['DELETE']) +@v2_bp.route('//blobs/', methods=['DELETE']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def delete_digest(namespace, repo_name, upload_uuid): diff --git a/endpoints/v2/manifest.py b/endpoints/v2/manifest.py index 6a6d5cb13..f09f365d5 100644 --- a/endpoints/v2/manifest.py +++ b/endpoints/v2/manifest.py @@ -1,6 +1,7 @@ import logging import jwt.utils import json +import features from peewee import IntegrityError from flask import make_response, request, url_for @@ -8,25 +9,25 @@ from collections import namedtuple, OrderedDict from jwkest.jws import SIGNER_ALGS, keyrep from datetime import datetime -from app import docker_v2_signing_key +from app import docker_v2_signing_key, app from auth.registry_jwt_auth import process_registry_jwt_auth from endpoints.decorators import anon_protect from endpoints.v2 import v2_bp, require_repo_read, require_repo_write -from endpoints.v2.errors import (BlobUnknown, ManifestInvalid, ManifestUnverified, - ManifestUnknown, TagInvalid, NameInvalid) +from endpoints.v2.errors import (BlobUnknown, ManifestInvalid, ManifestUnknown, TagInvalid, + NameInvalid) from endpoints.trackhelper import track_and_log from endpoints.notificationhelper import spawn_notification from digest import digest_tools from data import model from data.database import RepositoryTag - +from endpoints.common import parse_repository_name logger = logging.getLogger(__name__) VALID_TAG_PATTERN = r'[\w][\w.-]{0,127}' -BASE_MANIFEST_ROUTE = '///manifests/' +BASE_MANIFEST_ROUTE = '//manifests/' MANIFEST_DIGEST_ROUTE = BASE_MANIFEST_ROUTE.format(digest_tools.DIGEST_PATTERN) MANIFEST_TAGNAME_ROUTE = BASE_MANIFEST_ROUTE.format(VALID_TAG_PATTERN) @@ -61,9 +62,17 @@ class SignedManifest(object): self._parsed = json.loads(manifest_bytes) self._signatures = self._parsed[_SIGNATURES_KEY] - self._namespace, self._repo_name = self._parsed[_REPO_NAME_KEY].split('/') self._tag = self._parsed[_REPO_TAG_KEY] + repo_name_tuple = self._parsed[_REPO_NAME_KEY].split('/') + if len(repo_name_tuple) > 1: + self._namespace, self._repo_name = repo_name_tuple + elif len(repo_name_tuple) == 1: + self._namespace = '' + self._repo_name = repo_name_tuple[0] + else: + raise ValueError('repo_name has too many or too few pieces') + self._validate() def _validate(self): @@ -144,9 +153,13 @@ class SignedManifestBuilder(object): """ Class which represents a manifest which is currently being built. """ def __init__(self, namespace, repo_name, tag, architecture='amd64', schema_ver=1): + repo_name_key = '{0}/{1}'.format(namespace, repo_name) + if namespace == '': + repo_name_key = repo_name + self._base_payload = { _REPO_TAG_KEY: tag, - _REPO_NAME_KEY: '{0}/{1}'.format(namespace, repo_name), + _REPO_NAME_KEY: repo_name_key, _ARCH_KEY: architecture, _SCHEMA_VER: schema_ver, } @@ -213,6 +226,7 @@ class SignedManifestBuilder(object): @v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['GET']) @process_registry_jwt_auth +@parse_repository_name @require_repo_read @anon_protect def fetch_manifest_by_tagname(namespace, repo_name, manifest_ref): @@ -242,6 +256,7 @@ def fetch_manifest_by_tagname(namespace, repo_name, manifest_ref): @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['GET']) @process_registry_jwt_auth +@parse_repository_name @require_repo_read @anon_protect def fetch_manifest_by_digest(namespace, repo_name, manifest_ref): @@ -262,12 +277,13 @@ def fetch_manifest_by_digest(namespace, repo_name, manifest_ref): @v2_bp.route(MANIFEST_TAGNAME_ROUTE, methods=['PUT']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def write_manifest_by_tagname(namespace, repo_name, manifest_ref): try: manifest = SignedManifest(request.data) - except ValueError: + except ValueError as ve: raise ManifestInvalid() if manifest.tag != manifest_ref: @@ -278,6 +294,7 @@ def write_manifest_by_tagname(namespace, repo_name, manifest_ref): @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['PUT']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def write_manifest_by_digest(namespace, repo_name, manifest_ref): @@ -293,8 +310,16 @@ def write_manifest_by_digest(namespace, repo_name, manifest_ref): def _write_manifest(namespace, repo_name, manifest): - # Ensure that the manifest is for this repository. - if manifest.namespace != namespace or manifest.repo_name != repo_name: + # Ensure that the manifest is for this repository. If the manifest's namespace is empty, then + # it is for the library namespace and we need an extra check. + if (manifest.namespace == '' and features.LIBRARY_SUPPORT and + namespace == app.config['LIBRARY_NAMESPACE']): + # This is a library manifest. All good. + pass + elif manifest.namespace != namespace: + raise NameInvalid() + + if manifest.repo_name != repo_name: raise NameInvalid() # Ensure that the repository exists. @@ -369,13 +394,15 @@ def _write_manifest(namespace, repo_name, manifest): response = make_response('OK', 202) response.headers['Docker-Content-Digest'] = manifest_digest - response.headers['Location'] = url_for('v2.fetch_manifest_by_digest', namespace=namespace, - repo_name=repo_name, manifest_ref=manifest_digest) + response.headers['Location'] = url_for('v2.fetch_manifest_by_digest', + repository='%s/%s' % (namespace, repo_name), + manifest_ref=manifest_digest) return response @v2_bp.route(MANIFEST_DIGEST_ROUTE, methods=['DELETE']) @process_registry_jwt_auth +@parse_repository_name @require_repo_write @anon_protect def delete_manifest_by_digest(namespace, repo_name, manifest_ref): @@ -406,8 +433,14 @@ def _generate_and_store_manifest(namespace, repo_name, tag_name): image = model.tag.get_tag_image(namespace, repo_name, tag_name) parents = model.image.get_parent_images(namespace, repo_name, image) + # If the manifest is being generated under the library namespace, then we make its namespace + # empty. + manifest_namespace = namespace + if features.LIBRARY_SUPPORT and namespace == app.config['LIBRARY_NAMESPACE']: + manifest_namespace = '' + # Create and populate the manifest builder - builder = SignedManifestBuilder(namespace, repo_name, tag_name) + builder = SignedManifestBuilder(manifest_namespace, repo_name, tag_name) # Add the leaf layer builder.add_layer(image.storage.content_checksum, image.v1_json_metadata) diff --git a/endpoints/v2/tag.py b/endpoints/v2/tag.py index a1811b063..03aa51198 100644 --- a/endpoints/v2/tag.py +++ b/endpoints/v2/tag.py @@ -6,9 +6,11 @@ from endpoints.v2.errors import NameUnknown from endpoints.v2.v2util import add_pagination from endpoints.decorators import anon_protect from data import model +from endpoints.common import parse_repository_name -@v2_bp.route('///tags/list', methods=['GET']) +@v2_bp.route('//tags/list', methods=['GET']) @process_registry_jwt_auth +@parse_repository_name @require_repo_read @anon_protect def list_all_tags(namespace, repo_name): @@ -17,8 +19,7 @@ def list_all_tags(namespace, repo_name): raise NameUnknown() query = model.tag.list_repository_tags(namespace, repo_name) - - url = url_for('v2.list_all_tags', namespace=namespace, repo_name=repo_name) + url = url_for('v2.list_all_tags', repository='%s/%s' % (namespace, repo_name)) link, query = add_pagination(query, url) response = jsonify({ diff --git a/endpoints/v2/v2auth.py b/endpoints/v2/v2auth.py index 655217d46..3205ea6cc 100644 --- a/endpoints/v2/v2auth.py +++ b/endpoints/v2/v2auth.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) TOKEN_VALIDITY_LIFETIME_S = 60 * 60 # 1 hour SCOPE_REGEX = re.compile( - r'^repository:([\.a-zA-Z0-9_\-]+/[\.a-zA-Z0-9_\-]+):(((push|pull|\*),)*(push|pull|\*))$' + r'^repository:(([\.a-zA-Z0-9_\-]+/)?[\.a-zA-Z0-9_\-]+):(((push|pull|\*),)*(push|pull|\*))$' ) @lru_cache(maxsize=1) @@ -74,9 +74,10 @@ def generate_registry_jwt(): logger.debug('Match: %s', match.groups()) namespace_and_repo = match.group(1) - actions = match.group(2).split(',') + actions = match.group(3).split(',') - namespace, reponame = parse_namespace_repository(namespace_and_repo) + lib_namespace = app.config['LIBRARY_NAMESPACE'] + namespace, reponame = parse_namespace_repository(namespace_and_repo, lib_namespace) # Ensure that we are never creating an invalid repository. if not REPOSITORY_NAME_REGEX.match(reponame): diff --git a/endpoints/verbs.py b/endpoints/verbs.py index 568a469a9..79253002b 100644 --- a/endpoints/verbs.py +++ b/endpoints/verbs.py @@ -22,7 +22,7 @@ from formats.squashed import SquashedDockerImage from formats.aci import ACIImage from storage import Storage from endpoints.v2.blob import BLOB_DIGEST_ROUTE -from endpoints.common import route_show_if +from endpoints.common import route_show_if, parse_repository_name verbs = Blueprint('verbs', __name__) @@ -376,6 +376,7 @@ def get_squashed_tag(namespace, repository, tag): @anon_protect @verbs.route('/torrent{0}'.format(BLOB_DIGEST_ROUTE), methods=['GET']) @process_auth +@parse_repository_name def get_tag_torrent(namespace, repo_name, digest): permission = ReadRepositoryPermission(namespace, repo_name) public_repo = model.repository.repository_is_public(namespace, repo_name) diff --git a/endpoints/web.py b/endpoints/web.py index c3a1136d9..bad2a75ef 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -25,14 +25,14 @@ from buildtrigger.triggerutil import TriggerProviderException from data import model from data.database import db from endpoints.api.discovery import swagger_route_data -from endpoints.common import common_login, render_page_template, route_show_if, param_required +from endpoints.common import (common_login, render_page_template, route_show_if, param_required, + parse_repository_name, parse_repository_name_and_tag) from endpoints.csrf import csrf_protect, generate_csrf_token, verify_csrf from endpoints.decorators import anon_protect, anon_allowed from health.healthcheck import get_healthchecker from util.cache import no_cache, cache_control from util.headers import parse_basic_auth from util.invoice import renderInvoiceToPdf -from util.names import parse_repository_name, parse_repository_name_and_tag from util.seo import render_snapshot from util.systemlogs import build_logs_archive from util.useremails import send_email_changed @@ -409,7 +409,7 @@ def confirm_recovery(): abort(403) -@web.route('/repository//status', methods=['GET']) +@web.route('/repository//status', methods=['GET']) @parse_repository_name @anon_protect def build_status_badge(namespace, repository): @@ -597,7 +597,7 @@ def download_logs_archive(): abort(403) -@web.route('/bitbucket/setup/', methods=['GET']) +@web.route('/bitbucket/setup/', methods=['GET']) @require_session_login @parse_repository_name @route_show_if(features.BITBUCKET_BUILD) @@ -630,7 +630,7 @@ def attach_bitbucket_trigger(namespace, repository_name): abort(403) -@web.route('/customtrigger/setup/', methods=['GET']) +@web.route('/customtrigger/setup/', methods=['GET']) @require_session_login @parse_repository_name def attach_custom_build_trigger(namespace, repository_name): @@ -653,7 +653,7 @@ def attach_custom_build_trigger(namespace, repository_name): abort(403) -@web.route('/') +@web.route('/') @no_cache @process_oauth @parse_repository_name_and_tag diff --git a/endpoints/webhooks.py b/endpoints/webhooks.py index cb13f9fd9..abe43280c 100644 --- a/endpoints/webhooks.py +++ b/endpoints/webhooks.py @@ -67,7 +67,7 @@ def stripe_webhook(): return make_response('Okay') -@webhooks.route('/push//trigger/', methods=['POST']) +@webhooks.route('/push//trigger/', methods=['POST']) @webhooks.route('/push/trigger/', methods=['POST'], defaults={'repository': ''}) @process_auth def build_trigger_webhook(trigger_uuid, **kwargs): diff --git a/initdb.py b/initdb.py index 776ca165a..0e79a4d37 100644 --- a/initdb.py +++ b/initdb.py @@ -537,6 +537,9 @@ def populate_database(minimal=False): org.stripe_id = TEST_STRIPE_ID org.save() + liborg = model.organization.create_organization('library', 'quay+library@devtable.com', new_user_1) + liborg.save() + model.user.create_robot('coolrobot', org) oauth_app_1 = model.oauth.create_application(org, 'Some Test App', 'http://localhost:8000', diff --git a/test/data/test.db b/test/data/test.db index 4a72f1686551241b896f463d0d4156bfd51b74ab..c65ad698795c280dec1a4b80cedfc59140e15240 100644 GIT binary patch delta 58619 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