Merge pull request #2547 from coreos-inc/disable-for-trust

Disable certain APIs and build triggers when trust is enabled
This commit is contained in:
josephschorr 2017-04-17 13:05:16 -04:00 committed by GitHub
commit e79555ef95
8 changed files with 102 additions and 10 deletions

View file

@ -12,6 +12,8 @@ from flask_restful import Resource, abort, Api, reqparse
from flask_restful.utils.cors import crossdomain
from jsonschema import validate, ValidationError
import features
from app import app, metric_queue
from data import model
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
@ -373,6 +375,23 @@ def define_json_response(schema_name):
return wrapper
def disallow_under_trust(func):
""" Disallows the decorated operation for repository when it has trust enabled.
"""
@wraps(func)
def wrapper(self, *args, **kwargs):
if features.SIGNING:
namespace = args[0]
repository = args[1]
repo = model.repository.get_repository(namespace, repository)
if repo is not None and repo.trust_enabled:
raise InvalidRequest('Cannot call this method on a repostory with trust enabled')
return func(self, *args, **kwargs)
return wrapper
import endpoints.api.billing
import endpoints.api.build
import endpoints.api.discovery

View file

@ -19,7 +19,8 @@ from data.buildlogs import BuildStatusRetrievalError
from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource,
require_repo_read, require_repo_write, validate_json_request,
ApiResource, internal_only, format_date, api, path_param,
require_repo_admin, abort, disallow_for_app_repositories)
require_repo_admin, abort, disallow_for_app_repositories,
disallow_under_trust)
from endpoints.building import start_build, PreparedBuild, MaximumBuildsQueuedException
from endpoints.exception import Unauthorized, NotFound, InvalidRequest
from util.names import parse_robot_username
@ -225,6 +226,7 @@ class RepositoryBuildList(RepositoryParamResource):
@require_repo_write
@nickname('requestRepoBuild')
@disallow_for_app_repositories
@disallow_under_trust
@validate_json_request('RepositoryBuildRequest')
def post(self, namespace, repository):
""" Request that a repository be built and pushed from the specified input. """
@ -361,6 +363,7 @@ class RepositoryBuildResource(RepositoryParamResource):
@require_repo_admin
@nickname('cancelRepoBuild')
@disallow_under_trust
@disallow_for_app_repositories
def delete(self, namespace, repository, build_uuid):
""" Cancels a repository build. """

View file

@ -11,12 +11,13 @@ from flask import request, abort
from app import dockerfile_build_queue, tuf_metadata_api
from data import model, oci_model
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
from endpoints.api import (format_date, nickname, log_action, validate_json_request,
require_repo_read, require_repo_write, require_repo_admin,
RepositoryParamResource, resource, query_param, parse_args, ApiResource,
request_error, require_scope, path_param, page_support, parse_args,
query_param, truthy_bool, disallow_for_app_repositories, show_if)
from endpoints.exception import Unauthorized, NotFound, InvalidRequest, ExceedsLicenseException, DownstreamIssue
RepositoryParamResource, resource, parse_args, ApiResource,
request_error, require_scope, path_param, page_support,
query_param, truthy_bool, show_if)
from endpoints.exception import (Unauthorized, NotFound, InvalidRequest, ExceedsLicenseException,
DownstreamIssue)
from endpoints.api.billing import lookup_allowed_private_repos, get_namespace_plan
from endpoints.api.subscribe import check_repository_usage
@ -502,7 +503,7 @@ class RepositoryTrust(RepositoryParamResource):
values = request.get_json()
model.repository.set_trust(repo, values['trust_enabled'])
log_action('change_repo_trust', namespace,
{'repo': repository, 'namespace': namespace, 'trust_enabled': values['trust_enabled']},
repo=repo)

View file

@ -5,7 +5,7 @@ from flask import request, abort
from endpoints.api import (resource, nickname, require_repo_read, require_repo_write,
RepositoryParamResource, log_action, validate_json_request,
path_param, parse_args, query_param, truthy_bool,
disallow_for_app_repositories)
disallow_for_app_repositories, disallow_under_trust)
from endpoints.exception import NotFound
from endpoints.api.image import image_view
from data import model
@ -85,6 +85,7 @@ class RepositoryTag(RepositoryParamResource):
@require_repo_write
@disallow_for_app_repositories
@disallow_under_trust
@nickname('changeTagImage')
@validate_json_request('MoveTag')
def put(self, namespace, repository, tag):
@ -120,6 +121,7 @@ class RepositoryTag(RepositoryParamResource):
@require_repo_write
@disallow_for_app_repositories
@disallow_under_trust
@nickname('deleteFullTag')
def delete(self, namespace, repository, tag):
""" Delete the specified repository tag. """
@ -212,6 +214,7 @@ class RestoreTag(RepositoryParamResource):
@require_repo_write
@disallow_for_app_repositories
@disallow_under_trust
@nickname('restoreTag')
@validate_json_request('RestoreTag')
def post(self, namespace, repository, tag):

View file

@ -0,0 +1,50 @@
import pytest
from data import model
from endpoints.api.build import RepositoryBuildList, RepositoryBuildResource
from endpoints.api.tag import RepositoryTag, RestoreTag
from endpoints.api.trigger import (BuildTrigger, BuildTriggerSubdirs,
BuildTriggerActivate, BuildTriggerAnalyze, ActivateBuildTrigger,
BuildTriggerFieldValues, BuildTriggerSources,
BuildTriggerSourceNamespaces)
from endpoints.api.test.shared import client_with_identity, conduct_api_call
from test.fixtures import app, appconfig, database_uri, init_db_path, sqlitedb_file
BUILD_ARGS = {'build_uuid': '1234'}
IMAGE_ARGS = {'imageid': '1234', 'image_id': 1234}
MANIFEST_ARGS = {'manifestref': 'sha256:abcd1234'}
LABEL_ARGS = {'manifestref': 'sha256:abcd1234', 'labelid': '1234'}
NOTIFICATION_ARGS = {'uuid': '1234'}
TAG_ARGS = {'tag': 'foobar'}
TRIGGER_ARGS = {'trigger_uuid': '1234'}
FIELD_ARGS = {'trigger_uuid': '1234', 'field_name': 'foobar'}
@pytest.mark.parametrize('resource, method, params', [
(RepositoryBuildList, 'post', None),
(RepositoryBuildResource, 'delete', BUILD_ARGS),
(RepositoryTag, 'put', TAG_ARGS),
(RepositoryTag, 'delete', TAG_ARGS),
(RestoreTag, 'post', TAG_ARGS),
(BuildTrigger, 'delete', TRIGGER_ARGS),
(BuildTriggerSubdirs, 'post', TRIGGER_ARGS),
(BuildTriggerActivate, 'post', TRIGGER_ARGS),
(BuildTriggerAnalyze, 'post', TRIGGER_ARGS),
(ActivateBuildTrigger, 'post', TRIGGER_ARGS),
(BuildTriggerFieldValues, 'post', FIELD_ARGS),
(BuildTriggerSources, 'post', TRIGGER_ARGS),
(BuildTriggerSourceNamespaces, 'get', TRIGGER_ARGS),
])
def test_disallowed_for_apps(resource, method, params, client):
namespace = 'devtable'
repository = 'somerepo'
devtable = model.user.get_user('devtable')
repo = model.repository.create_repository(namespace, repository, devtable, repo_kind='image')
model.repository.set_trust(repo, True)
params = params or {}
params['repository'] = '%s/%s' % (namespace, repository)
with client_with_identity('devtable', client) as cl:
conduct_api_call(cl, resource, method, params, None, 400)

View file

@ -20,8 +20,7 @@ from data.model.build import update_build_trigger
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
log_action, request_error, query_param, parse_args, internal_only,
validate_json_request, api, path_param, abort,
disallow_for_app_repositories)
from endpoints.exception import NotFound, Unauthorized, InvalidRequest
disallow_for_app_repositories, disallow_under_trust)
from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus
from endpoints.building import start_build, MaximumBuildsQueuedException
from endpoints.exception import NotFound, Unauthorized, InvalidRequest
@ -72,6 +71,7 @@ class BuildTrigger(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('deleteBuildTrigger')
def delete(self, namespace_name, repo_name, trigger_uuid):
""" Delete the specified build trigger. """
@ -116,6 +116,7 @@ class BuildTriggerSubdirs(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('listBuildTriggerSubdirs')
@validate_json_request('BuildTriggerSubdirRequest')
def post(self, namespace_name, repo_name, trigger_uuid):
@ -183,6 +184,7 @@ class BuildTriggerActivate(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('activateBuildTrigger')
@validate_json_request('BuildTriggerActivateRequest')
def post(self, namespace_name, repo_name, trigger_uuid):
@ -283,6 +285,7 @@ class BuildTriggerAnalyze(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('analyzeBuildTrigger')
@validate_json_request('BuildTriggerAnalyzeRequest')
def post(self, namespace_name, repo_name, trigger_uuid):
@ -464,6 +467,7 @@ class ActivateBuildTrigger(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('manuallyStartBuildTrigger')
@validate_json_request('RunParameters')
def post(self, namespace_name, repo_name, trigger_uuid):
@ -528,6 +532,7 @@ class BuildTriggerFieldValues(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('listTriggerFieldValues')
def post(self, namespace_name, repo_name, trigger_uuid, field_name):
""" List the field values for a custom run field. """
@ -572,6 +577,7 @@ class BuildTriggerSources(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('listTriggerBuildSources')
@validate_json_request('BuildTriggerSourcesRequest')
def post(self, namespace_name, repo_name, trigger_uuid):
@ -606,6 +612,7 @@ class BuildTriggerSourceNamespaces(RepositoryParamResource):
@require_repo_admin
@disallow_for_app_repositories
@disallow_under_trust
@nickname('listTriggerBuildSourceNamespaces')
def get(self, namespace_name, repo_name, trigger_uuid):
""" List the build sources for the trigger configuration thus far. """

View file

@ -647,6 +647,8 @@ def attach_bitbucket_trigger(namespace_name, repo_name):
abort(404, message=msg)
elif repo.kind.name != 'image':
abort(501)
elif repo.trust_enabled:
abort(400)
trigger = model.build.create_build_trigger(repo, BitbucketBuildTrigger.service_name(), None,
current_user.db_user())
@ -682,6 +684,8 @@ def attach_custom_build_trigger(namespace_name, repo_name):
abort(404, message=msg)
elif repo.kind.name != 'image':
abort(501)
elif repo.trust_enabled:
abort(400)
trigger = model.build.create_build_trigger(repo, CustomBuildTrigger.service_name(),
None, current_user.db_user())

View file

@ -87,6 +87,11 @@ def build_trigger_webhook(trigger_uuid, **kwargs):
if permission.can():
handler = BuildTriggerHandler.get_handler(trigger)
if trigger.repository.kind.name != 'image':
abort(501, 'Build triggers cannot be invoked on application repositories')
elif trigger.repository.trust_enabled:
abort(400, 'Build triggers cannot be invoked on repositories with trust enabled')
logger.debug('Passing webhook request to handler %s', handler)
try:
prepared = handler.handle_trigger_request(request)