Add a feature flag for disabling unauthenticated access to the registry in its entirety.

This commit is contained in:
Joseph Schorr 2015-05-19 17:52:44 -04:00
parent 598fc6ec46
commit 54992c23b7
15 changed files with 147 additions and 25 deletions

View file

@ -19,6 +19,7 @@ from auth import scopes
from auth.auth_context import get_authenticated_user, get_validated_oauth_token
from auth.auth import process_oauth
from endpoints.csrf import csrf_protect
from endpoints.decorators import anon_protect
logger = logging.getLogger(__name__)
@ -228,12 +229,14 @@ def parse_repository_name(func):
class ApiResource(Resource):
method_decorators = [anon_protect]
def options(self):
return None, 200
class RepositoryParamResource(ApiResource):
method_decorators = [parse_repository_name]
method_decorators = [anon_protect, parse_repository_name]
def require_repo_permission(permission_class, scope, allow_public=False):

View file

@ -15,6 +15,7 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques
RepositoryParamResource)
from endpoints.api.subscribe import subscribe
from endpoints.common import common_login
from endpoints.decorators import anon_allowed
from endpoints.api.team import try_accept_invite
from data import model
@ -203,6 +204,7 @@ class User(ApiResource):
@require_scope(scopes.READ_USER)
@nickname('getLoggedInUser')
@define_json_response('UserView')
@anon_allowed
def get(self):
""" Get user information for the authenticated user. """
user = get_authenticated_user()
@ -498,6 +500,7 @@ class Signin(ApiResource):
@nickname('signinUser')
@validate_json_request('SigninUser')
@anon_allowed
def post(self):
""" Sign in the user with the specified credentials. """
signin_data = request.get_json()

30
endpoints/decorators.py Normal file
View file

@ -0,0 +1,30 @@
""" Various decorators for endpoint and API handlers. """
import features
from flask import abort
from auth.auth_context import (get_validated_oauth_token, get_authenticated_user,
get_validated_token, get_grant_user_context)
from functools import wraps
def anon_allowed(func):
""" Marks a method to allow anonymous access where it would otherwise be disallowed. """
func.__anon_allowed = True
return func
def anon_protect(func):
""" Marks a method as requiring some form of valid user auth before it can be executed. """
@wraps(func)
def wrapper(*args, **kwargs):
# Skip if anonymous access is allowed.
if features.ANONYMOUS_ACCESS or '__anon_allowed' in dir(func):
return func(*args, **kwargs)
# Check for validated context. If none exists, fail with a 401.
if (get_authenticated_user() or get_validated_oauth_token() or get_validated_token() or
get_grant_user_context()):
return func(*args, **kwargs)
abort(401)
return wrapper

View file

@ -20,6 +20,7 @@ from auth.permissions import (ModifyRepositoryPermission, UserAdminPermission,
from util.http import abort
from endpoints.trackhelper import track_and_log
from endpoints.notificationhelper import spawn_notification
from endpoints.decorators import anon_protect
import features
@ -278,6 +279,7 @@ def update_images(namespace, repository):
@process_auth
@parse_repository_name
@generate_headers(scope=GrantType.READ_REPOSITORY)
@anon_protect
def get_repository_images(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@ -315,6 +317,7 @@ def put_repository_auth(namespace, repository):
@index.route('/search', methods=['GET'])
@process_auth
@anon_protect
def get_search():
def result_view(repo):
return {

View file

@ -16,6 +16,7 @@ from auth.permissions import (ReadRepositoryPermission,
ModifyRepositoryPermission)
from data import model, database
from util import gzipstream
from endpoints.decorators import anon_protect
registry = Blueprint('registry', __name__)
@ -97,6 +98,7 @@ def set_cache_headers(f):
@extract_namespace_repo_from_session
@require_completion
@set_cache_headers
@anon_protect
def head_image_layer(namespace, repository, image_id, headers):
permission = ReadRepositoryPermission(namespace, repository)
@ -130,6 +132,7 @@ def head_image_layer(namespace, repository, image_id, headers):
@extract_namespace_repo_from_session
@require_completion
@set_cache_headers
@anon_protect
def get_image_layer(namespace, repository, image_id, headers):
permission = ReadRepositoryPermission(namespace, repository)
@ -352,6 +355,7 @@ def put_image_checksum(namespace, repository, image_id):
@extract_namespace_repo_from_session
@require_completion
@set_cache_headers
@anon_protect
def get_image_json(namespace, repository, image_id, headers):
logger.debug('Checking repo permissions')
permission = ReadRepositoryPermission(namespace, repository)
@ -383,6 +387,7 @@ def get_image_json(namespace, repository, image_id, headers):
@extract_namespace_repo_from_session
@require_completion
@set_cache_headers
@anon_protect
def get_image_ancestry(namespace, repository, image_id, headers):
logger.debug('Checking repo permissions')
permission = ReadRepositoryPermission(namespace, repository)

View file

@ -10,6 +10,7 @@ from auth.auth import process_auth
from auth.permissions import (ReadRepositoryPermission,
ModifyRepositoryPermission)
from data import model
from endpoints.decorators import anon_protect
logger = logging.getLogger(__name__)
@ -20,6 +21,7 @@ tags = Blueprint('tags', __name__)
@tags.route('/repositories/<path:repository>/tags',
methods=['GET'])
@process_auth
@anon_protect
@parse_repository_name
def get_tags(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@ -35,6 +37,7 @@ def get_tags(namespace, repository):
@tags.route('/repositories/<path:repository>/tags/<tag>',
methods=['GET'])
@process_auth
@anon_protect
@parse_repository_name
def get_tag(namespace, repository, tag):
permission = ReadRepositoryPermission(namespace, repository)

View file

@ -10,6 +10,7 @@ from auth.permissions import ReadRepositoryPermission
from data import model
from data import database
from endpoints.trackhelper import track_and_log
from endpoints.decorators import anon_protect
from storage import Storage
from util.queuefile import QueueFile
@ -256,6 +257,7 @@ def os_arch_checker(os, arch):
return checker
@anon_protect
@verbs.route('/aci/<server>/<namespace>/<repository>/<tag>/sig/<os>/<arch>/', methods=['GET'])
@verbs.route('/aci/<server>/<namespace>/<repository>/<tag>/aci.asc/<os>/<arch>/', methods=['GET'])
@process_auth
@ -264,6 +266,7 @@ def get_aci_signature(server, namespace, repository, tag, os, arch):
os=os, arch=arch)
@anon_protect
@verbs.route('/aci/<server>/<namespace>/<repository>/<tag>/aci/<os>/<arch>/', methods=['GET'])
@process_auth
def get_aci_image(server, namespace, repository, tag, os, arch):
@ -271,6 +274,7 @@ def get_aci_image(server, namespace, repository, tag, os, arch):
sign=True, checker=os_arch_checker(os, arch), os=os, arch=arch)
@anon_protect
@verbs.route('/squash/<namespace>/<repository>/<tag>', methods=['GET'])
@process_auth
def get_squashed_tag(namespace, repository, tag):

View file

@ -18,6 +18,7 @@ from util.invoice import renderInvoiceToPdf
from util.seo import render_snapshot
from util.cache import no_cache
from endpoints.common import common_login, render_page_template, route_show_if, param_required
from endpoints.decorators import anon_protect
from endpoints.csrf import csrf_protect, generate_csrf_token, verify_csrf
from endpoints.registry import set_cache_headers
from endpoints.trigger import (CustomBuildTrigger, BitbucketBuildTrigger, TriggerProviderException,
@ -79,6 +80,7 @@ def snapshot(path = ''):
@web.route('/aci-signing-key')
@no_cache
@anon_protect
def aci_signing_key():
if not signer.name:
abort(404)
@ -337,6 +339,7 @@ def confirm_recovery():
@web.route('/repository/<path:repository>/status', methods=['GET'])
@parse_repository_name
@no_cache
@anon_protect
def build_status_badge(namespace, repository):
token = request.args.get('token', None)
is_public = model.repository_is_public(namespace, repository)
@ -565,6 +568,7 @@ def attach_custom_build_trigger(namespace, repository_name):
@no_cache
@process_oauth
@parse_repository_name_and_tag
@anon_protect
def redirect_to_repository(namespace, reponame, tag):
permission = ReadRepositoryPermission(namespace, reponame)
is_public = model.repository_is_public(namespace, reponame)
@ -582,6 +586,7 @@ def redirect_to_repository(namespace, reponame, tag):
@web.route('/<namespace>')
@no_cache
@process_oauth
@anon_protect
def redirect_to_namespace(namespace):
user_or_org = model.get_user_or_org(namespace)
if not user_or_org: