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_context() and not get_authenticated_context().is_anonymous: return func(*args, **kwargs) abort(401, message='Anonymous access is not allowed')
def abort(status_code, message=None, issue=None, headers=None, **kwargs): message = str(message) % kwargs if message else DEFAULT_MESSAGE.get( status_code, "") params = dict(request.view_args or {}) params.update(kwargs) params["url"] = request.url params["status_code"] = status_code params["message"] = message # Add the user information. auth_context = get_authenticated_context() if auth_context is not None: message = "%s (authorized: %s)" % (message, auth_context.description) # Log the abort. logger.error("Error %s: %s; Arguments: %s" % (status_code, message, params)) # Create the final response data and message. data = {} data["error"] = message if headers is None: headers = {} _abort(status_code, data, message, headers)
def get_user(): context = get_authenticated_context() if not context or context.is_anonymous: abort(404) return jsonify({ "username": context.credential_username, "email": None, })
def catalog_search(start_id, limit, pagination_callback): def _load_catalog(): include_public = bool(features.PUBLIC_CATALOG) if not include_public and not get_authenticated_user(): return [] username = get_authenticated_user().username if get_authenticated_user( ) else None if username and not get_authenticated_user().enabled: return [] query = model.repository.get_visible_repositories( username, kind_filter="image", include_public=include_public, start_id=start_id, limit=limit + 1, ) # NOTE: The repository ID is in `rid` (not `id`) here, as per the requirements of # the `get_visible_repositories` call. return [ Repository(repo.rid, repo.namespace_user.username, repo.name)._asdict() for repo in query ] context_key = get_authenticated_context( ).unique_key if get_authenticated_context() else None catalog_cache_key = cache_key.for_catalog_page(context_key, start_id, limit, model_cache.cache_config) visible_repositories = [ Repository(**repo_dict) for repo_dict in model_cache.retrieve(catalog_cache_key, _load_catalog) ] response = jsonify({ "repositories": [ "%s/%s" % (repo.namespace_name, repo.name) for repo in visible_repositories ][0:limit], }) pagination_callback(visible_repositories, response) return response
def test_apply_context(get_entity, entity_kind, app): assert get_authenticated_context() is None entity = get_entity() args = {} args[entity_kind] = entity result = ValidateResult(AuthKind.basic, **args) result.apply_to_context() expected_user = entity if entity_kind == 'user' or entity_kind == 'robot' else None if entity_kind == 'oauthtoken': expected_user = entity.authorized_user if entity_kind == 'appspecifictoken': expected_user = entity.user expected_token = entity if entity_kind == 'token' else None expected_oauth = entity if entity_kind == 'oauthtoken' else None expected_appspecifictoken = entity if entity_kind == 'appspecifictoken' else None expected_grant = entity if entity_kind == 'signed_data' else None assert get_authenticated_context().authed_user == expected_user assert get_authenticated_context().token == expected_token assert get_authenticated_context().oauthtoken == expected_oauth assert get_authenticated_context( ).appspecifictoken == expected_appspecifictoken assert get_authenticated_context().signed_data == expected_grant
def v2_support_enabled(): docker_ver = docker_version(request.user_agent.string) # Check if our version is one of the blacklisted versions, if we can't # identify the version (None) we will fail open and assume that it is # newer and therefore should not be blacklisted. if docker_ver is not None and Spec(app.config['BLACKLIST_V2_SPEC']).match(docker_ver): abort(404) response = make_response('true', 200) if get_authenticated_context() is None: response = make_response('true', 401) response.headers.extend(get_auth_headers()) return response
def abort(status_code, message=None, issue=None, headers=None, **kwargs): message = (str(message) % kwargs if message else DEFAULT_MESSAGE.get(status_code, '')) params = dict(request.view_args or {}) params.update(kwargs) params['url'] = request.url params['status_code'] = status_code params['message'] = message # Add the user information. auth_context = get_authenticated_context() if auth_context is not None: message = '%s (authorized: %s)' % (message, auth_context.description) # Log the abort. logger.error('Error %s: %s; Arguments: %s' % (status_code, message, params)) # Calculate the issue URL (if the issue ID was supplied). issue_url = None if issue: issue_url = 'http://docs.quay.io/issues/%s.html' % (issue) # Create the final response data and message. data = {} data['error'] = message if issue_url: data['info_url'] = issue_url if headers is None: headers = {} _abort(status_code, data, message, headers)
def generate_registry_jwt(auth_result): """ This endpoint will generate a JWT conforming to the Docker Registry v2 Auth Spec: https://docs.docker.com/registry/spec/auth/token/ """ audience_param = request.args.get("service") logger.debug("Request audience: %s", audience_param) scope_params = request.args.getlist("scope") or [] logger.debug("Scope request: %s", scope_params) auth_header = request.headers.get("authorization", "") auth_credentials_sent = bool(auth_header) # Load the auth context and verify thatg we've directly received credentials. has_valid_auth_context = False if get_authenticated_context(): has_valid_auth_context = not get_authenticated_context().is_anonymous if auth_credentials_sent and not has_valid_auth_context: # The auth credentials sent for the user are invalid. raise InvalidLogin(auth_result.error_message) if not has_valid_auth_context and len(scope_params) == 0: # In this case, we are doing an auth flow, and it's not an anonymous pull. logger.debug("No user and no token sent for empty scope list") raise Unauthorized() # Build the access list for the authenticated context. access = [] scope_results = [] for scope_param in scope_params: scope_result = _authorize_or_downscope_request(scope_param, has_valid_auth_context) if scope_result is None: continue scope_results.append(scope_result) access.append({ "type": "repository", "name": scope_result.registry_and_repo, "actions": scope_result.actions, }) # Issue user events. user_event_data = { "action": "login", } # Set the user event data for when authed. if len(scope_results) > 0: if "push" in scope_results[0].actions: user_action = "push_start" elif "pull" in scope_results[0].actions: user_action = "pull_start" else: user_action = "login" user_event_data = { "action": user_action, "namespace": scope_results[0].namespace, "repository": scope_results[0].repository, } # Send the user event. if get_authenticated_user() is not None: event = userevents.get_event(get_authenticated_user().username) event.publish_event_data("docker-cli", user_event_data) # Build the signed JWT. tuf_roots = { "%s/%s" % (scope_result.namespace, scope_result.repository): scope_result.tuf_root for scope_result in scope_results } context, subject = build_context_and_subject(get_authenticated_context(), tuf_roots=tuf_roots) token = generate_bearer_token(audience_param, subject, context, access, TOKEN_VALIDITY_LIFETIME_S, instance_keys) return jsonify({"token": token})
def track_and_log(event_name, repo_obj, analytics_name=None, analytics_sample=1, **kwargs): repo_name = repo_obj.name namespace_name = repo_obj.namespace_name metadata = { "repo": repo_name, "namespace": namespace_name, "user-agent": request.user_agent.string, } metadata.update(kwargs) is_free_namespace = False if hasattr(repo_obj, "is_free_namespace"): is_free_namespace = repo_obj.is_free_namespace # Add auth context metadata. analytics_id = "anonymous" auth_context = get_authenticated_context() if auth_context is not None: analytics_id, context_metadata = auth_context.analytics_id_and_public_metadata( ) metadata.update(context_metadata) # Publish the user event (if applicable) logger.debug("Checking publishing %s to the user events system", event_name) if auth_context and auth_context.has_nonrobot_user: logger.debug("Publishing %s to the user events system", event_name) user_event_data = { "action": event_name, "repository": repo_name, "namespace": namespace_name, } event = userevents.get_event(auth_context.authed_user.username) event.publish_event_data("docker-cli", user_event_data) # Save the action to mixpanel. if random.random() < analytics_sample: if analytics_name is None: analytics_name = event_name logger.debug("Logging the %s to analytics engine", analytics_name) request_parsed = urlparse(request.url_root) extra_params = { "repository": "%s/%s" % (namespace_name, repo_name), "user-agent": request.user_agent.string, "hostname": request_parsed.hostname, } analytics.track(analytics_id, analytics_name, extra_params) # Add the resolved information to the metadata. logger.debug("Resolving IP address %s", get_request_ip()) resolved_ip = ip_resolver.resolve_ip(get_request_ip()) if resolved_ip is not None: metadata["resolved_ip"] = resolved_ip._asdict() logger.debug("Resolved IP address %s", get_request_ip()) # Log the action to the database. logger.debug("Logging the %s to logs system", event_name) try: logs_model.log_action( event_name, namespace_name, performer=get_authenticated_user(), ip=get_request_ip(), metadata=metadata, repository=repo_obj, is_free_namespace=is_free_namespace, ) logger.debug("Track and log of %s complete", event_name) except ReadOnlyModeException: pass