Merge pull request #2687 from ecordell/enable-builds-trust
Re-enable builds and tag modification when signing is enabled
This commit is contained in:
commit
b6d423a50d
15 changed files with 39 additions and 118 deletions
|
@ -387,23 +387,6 @@ def define_json_response(schema_name):
|
||||||
return wrapper
|
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.billing
|
||||||
import endpoints.api.build
|
import endpoints.api.build
|
||||||
import endpoints.api.discovery
|
import endpoints.api.discovery
|
||||||
|
|
|
@ -19,8 +19,7 @@ from data.buildlogs import BuildStatusRetrievalError
|
||||||
from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource,
|
from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource,
|
||||||
require_repo_read, require_repo_write, validate_json_request,
|
require_repo_read, require_repo_write, validate_json_request,
|
||||||
ApiResource, internal_only, format_date, api, path_param,
|
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.building import start_build, PreparedBuild, MaximumBuildsQueuedException
|
||||||
from endpoints.exception import Unauthorized, NotFound, InvalidRequest
|
from endpoints.exception import Unauthorized, NotFound, InvalidRequest
|
||||||
from util.names import parse_robot_username
|
from util.names import parse_robot_username
|
||||||
|
@ -226,7 +225,6 @@ class RepositoryBuildList(RepositoryParamResource):
|
||||||
@require_repo_write
|
@require_repo_write
|
||||||
@nickname('requestRepoBuild')
|
@nickname('requestRepoBuild')
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@validate_json_request('RepositoryBuildRequest')
|
@validate_json_request('RepositoryBuildRequest')
|
||||||
def post(self, namespace, repository):
|
def post(self, namespace, repository):
|
||||||
""" Request that a repository be built and pushed from the specified input. """
|
""" Request that a repository be built and pushed from the specified input. """
|
||||||
|
@ -363,7 +361,6 @@ class RepositoryBuildResource(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@nickname('cancelRepoBuild')
|
@nickname('cancelRepoBuild')
|
||||||
@disallow_under_trust
|
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
def delete(self, namespace, repository, build_uuid):
|
def delete(self, namespace, repository, build_uuid):
|
||||||
""" Cancels a repository build. """
|
""" Cancels a repository build. """
|
||||||
|
|
|
@ -5,7 +5,7 @@ from flask import request, abort
|
||||||
from endpoints.api import (
|
from endpoints.api import (
|
||||||
resource, nickname, require_repo_read, require_repo_write, RepositoryParamResource, log_action,
|
resource, nickname, require_repo_read, require_repo_write, RepositoryParamResource, log_action,
|
||||||
validate_json_request, path_param, parse_args, query_param, truthy_bool,
|
validate_json_request, path_param, parse_args, query_param, truthy_bool,
|
||||||
disallow_for_app_repositories, disallow_under_trust)
|
disallow_for_app_repositories)
|
||||||
from endpoints.exception import NotFound
|
from endpoints.exception import NotFound
|
||||||
from endpoints.api.image import image_view
|
from endpoints.api.image import image_view
|
||||||
from endpoints.v2.manifest import _generate_and_store_manifest
|
from endpoints.v2.manifest import _generate_and_store_manifest
|
||||||
|
@ -87,7 +87,6 @@ class RepositoryTag(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_write
|
@require_repo_write
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('changeTagImage')
|
@nickname('changeTagImage')
|
||||||
@validate_json_request('MoveTag')
|
@validate_json_request('MoveTag')
|
||||||
def put(self, namespace, repository, tag):
|
def put(self, namespace, repository, tag):
|
||||||
|
@ -128,7 +127,6 @@ class RepositoryTag(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_write
|
@require_repo_write
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('deleteFullTag')
|
@nickname('deleteFullTag')
|
||||||
def delete(self, namespace, repository, tag):
|
def delete(self, namespace, repository, tag):
|
||||||
""" Delete the specified repository tag. """
|
""" Delete the specified repository tag. """
|
||||||
|
@ -225,7 +223,6 @@ class RestoreTag(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_write
|
@require_repo_write
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('restoreTag')
|
@nickname('restoreTag')
|
||||||
@validate_json_request('RestoreTag')
|
@validate_json_request('RestoreTag')
|
||||||
def post(self, namespace, repository, tag):
|
def post(self, namespace, repository, tag):
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
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 *
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ from data.model.build import update_build_trigger
|
||||||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||||
log_action, request_error, query_param, parse_args, internal_only,
|
log_action, request_error, query_param, parse_args, internal_only,
|
||||||
validate_json_request, api, path_param, abort,
|
validate_json_request, api, path_param, abort,
|
||||||
disallow_for_app_repositories, disallow_under_trust)
|
disallow_for_app_repositories)
|
||||||
from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus
|
from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus
|
||||||
from endpoints.api.trigger_analyzer import TriggerAnalyzer
|
from endpoints.api.trigger_analyzer import TriggerAnalyzer
|
||||||
from endpoints.building import start_build, MaximumBuildsQueuedException
|
from endpoints.building import start_build, MaximumBuildsQueuedException
|
||||||
|
@ -72,7 +72,6 @@ class BuildTrigger(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('deleteBuildTrigger')
|
@nickname('deleteBuildTrigger')
|
||||||
def delete(self, namespace_name, repo_name, trigger_uuid):
|
def delete(self, namespace_name, repo_name, trigger_uuid):
|
||||||
""" Delete the specified build trigger. """
|
""" Delete the specified build trigger. """
|
||||||
|
@ -114,7 +113,6 @@ class BuildTriggerSubdirs(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('listBuildTriggerSubdirs')
|
@nickname('listBuildTriggerSubdirs')
|
||||||
@validate_json_request('BuildTriggerSubdirRequest')
|
@validate_json_request('BuildTriggerSubdirRequest')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid):
|
def post(self, namespace_name, repo_name, trigger_uuid):
|
||||||
|
@ -179,7 +177,6 @@ class BuildTriggerActivate(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('activateBuildTrigger')
|
@nickname('activateBuildTrigger')
|
||||||
@validate_json_request('BuildTriggerActivateRequest')
|
@validate_json_request('BuildTriggerActivateRequest')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid):
|
def post(self, namespace_name, repo_name, trigger_uuid):
|
||||||
|
@ -276,7 +273,6 @@ class BuildTriggerAnalyze(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('analyzeBuildTrigger')
|
@nickname('analyzeBuildTrigger')
|
||||||
@validate_json_request('BuildTriggerAnalyzeRequest')
|
@validate_json_request('BuildTriggerAnalyzeRequest')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid):
|
def post(self, namespace_name, repo_name, trigger_uuid):
|
||||||
|
@ -339,7 +335,6 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('manuallyStartBuildTrigger')
|
@nickname('manuallyStartBuildTrigger')
|
||||||
@validate_json_request('RunParameters')
|
@validate_json_request('RunParameters')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid):
|
def post(self, namespace_name, repo_name, trigger_uuid):
|
||||||
|
@ -401,7 +396,6 @@ class BuildTriggerFieldValues(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('listTriggerFieldValues')
|
@nickname('listTriggerFieldValues')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid, field_name):
|
def post(self, namespace_name, repo_name, trigger_uuid, field_name):
|
||||||
""" List the field values for a custom run field. """
|
""" List the field values for a custom run field. """
|
||||||
|
@ -443,7 +437,6 @@ class BuildTriggerSources(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('listTriggerBuildSources')
|
@nickname('listTriggerBuildSources')
|
||||||
@validate_json_request('BuildTriggerSourcesRequest')
|
@validate_json_request('BuildTriggerSourcesRequest')
|
||||||
def post(self, namespace_name, repo_name, trigger_uuid):
|
def post(self, namespace_name, repo_name, trigger_uuid):
|
||||||
|
@ -475,7 +468,6 @@ class BuildTriggerSourceNamespaces(RepositoryParamResource):
|
||||||
|
|
||||||
@require_repo_admin
|
@require_repo_admin
|
||||||
@disallow_for_app_repositories
|
@disallow_for_app_repositories
|
||||||
@disallow_under_trust
|
|
||||||
@nickname('listTriggerBuildSourceNamespaces')
|
@nickname('listTriggerBuildSourceNamespaces')
|
||||||
def get(self, namespace_name, repo_name, trigger_uuid):
|
def get(self, namespace_name, repo_name, trigger_uuid):
|
||||||
""" List the build sources for the trigger configuration thus far. """
|
""" List the build sources for the trigger configuration thus far. """
|
||||||
|
|
|
@ -674,8 +674,6 @@ def attach_bitbucket_trigger(namespace_name, repo_name):
|
||||||
abort(404, message=msg)
|
abort(404, message=msg)
|
||||||
elif repo.kind.name != 'image':
|
elif repo.kind.name != 'image':
|
||||||
abort(501)
|
abort(501)
|
||||||
elif repo.trust_enabled:
|
|
||||||
abort(400)
|
|
||||||
|
|
||||||
trigger = model.build.create_build_trigger(repo, BitbucketBuildTrigger.service_name(), None,
|
trigger = model.build.create_build_trigger(repo, BitbucketBuildTrigger.service_name(), None,
|
||||||
current_user.db_user())
|
current_user.db_user())
|
||||||
|
@ -711,8 +709,6 @@ def attach_custom_build_trigger(namespace_name, repo_name):
|
||||||
abort(404, message=msg)
|
abort(404, message=msg)
|
||||||
elif repo.kind.name != 'image':
|
elif repo.kind.name != 'image':
|
||||||
abort(501)
|
abort(501)
|
||||||
elif repo.trust_enabled:
|
|
||||||
abort(400)
|
|
||||||
|
|
||||||
trigger = model.build.create_build_trigger(repo, CustomBuildTrigger.service_name(),
|
trigger = model.build.create_build_trigger(repo, CustomBuildTrigger.service_name(),
|
||||||
None, current_user.db_user())
|
None, current_user.db_user())
|
||||||
|
|
|
@ -89,8 +89,6 @@ def build_trigger_webhook(trigger_uuid, **kwargs):
|
||||||
|
|
||||||
if trigger.repository.kind.name != 'image':
|
if trigger.repository.kind.name != 'image':
|
||||||
abort(501, 'Build triggers cannot be invoked on application repositories')
|
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)
|
logger.debug('Passing webhook request to handler %s', handler)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -89,14 +89,14 @@
|
||||||
<i class="fa ci-robot"></i> New Robot Account
|
<i class="fa ci-robot"></i> New Robot Account
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write && !currentPageContext.repository.trust_enabled"></li>
|
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write && !currentPageContext.repository.tag_operations_disabled"></li>
|
||||||
<li role="presentation" class="dropdown-header"
|
<li role="presentation" class="dropdown-header"
|
||||||
ng-if="currentPageContext.repository && currentPageContext.repository.can_write &&
|
ng-if="currentPageContext.repository && currentPageContext.repository.can_write &&
|
||||||
!currentPageContext.repository.trust_enabled">
|
!currentPageContext.repository.tag_operations_disabled">
|
||||||
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
|
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
|
||||||
</li>
|
</li>
|
||||||
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write &&
|
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write &&
|
||||||
!currentPageContext.repository.trust_enabled">
|
!currentPageContext.repository.tag_operations_disabled">
|
||||||
<a ng-click="startBuild()">
|
<a ng-click="startBuild()">
|
||||||
<i class="fa fa-tasks"></i> New Dockerfile Build
|
<i class="fa fa-tasks"></i> New Dockerfile Build
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<div class="repo-panel-builds-element">
|
<div class="repo-panel-builds-element">
|
||||||
<div class="feedback-bar" feedback="feedback"></div>
|
<div class="feedback-bar" feedback="feedback"></div>
|
||||||
<div class="tab-header-controls">
|
<div class="tab-header-controls">
|
||||||
<button class="btn btn-primary" ng-click="showNewBuildDialog()" ng-if="!repository.trust_enabled">
|
<button class="btn btn-primary" ng-click="showNewBuildDialog()" ng-if="!repository.tag_operations_disabled">
|
||||||
<i class="fa fa-play"></i> Start New Build
|
<i class="fa fa-play"></i> Start New Build
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="tab-header">Repository Builds</h3>
|
<h3 class="tab-header">Repository Builds</h3>
|
||||||
|
|
||||||
<div class="co-alert co-alert-info" ng-if="repository.trust_enabled">
|
<div class="co-alert co-alert-info" ng-if="repository.tag_operations_disabled">
|
||||||
Builds cannot be performed on this repository because Quay Trust is
|
Builds cannot be performed on this repository because Quay Trust is
|
||||||
enabled, which requires that all operations be signed by a user.
|
enabled, which requires that all operations be signed by a user.
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
</div> <!-- /Builds -->
|
</div> <!-- /Builds -->
|
||||||
|
|
||||||
<!-- Build Triggers -->
|
<!-- Build Triggers -->
|
||||||
<div class="co-panel" ng-if="repository.can_admin && TriggerService.getTypes().length && !repository.trust_enabled" id="repoBuildTriggers">
|
<div class="co-panel" ng-if="repository.can_admin && TriggerService.getTypes().length && !repository.tag_operations_disabled" id="repoBuildTriggers">
|
||||||
<!-- Builds header controls -->
|
<!-- Builds header controls -->
|
||||||
<div class="co-panel-heading">
|
<div class="co-panel-heading">
|
||||||
<i class="fa fa-flash"></i>
|
<i class="fa fa-flash"></i>
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<!-- No Builds -->
|
<!-- No Builds -->
|
||||||
<div class="empty" ng-if="builds && !builds.length">
|
<div class="empty" ng-if="builds && !builds.length">
|
||||||
<div class="empty-primary-msg">No builds have been run for this repository.</div>
|
<div class="empty-primary-msg">No builds have been run for this repository.</div>
|
||||||
<div class="empty-secondary-msg" ng-if="repository.can_write && !repository.trust_enabled">
|
<div class="empty-secondary-msg" ng-if="repository.can_write && !repository.tag_operations_disabled">
|
||||||
Click on the <i class="fa fa-tasks" style="margin-left: 6px"></i> Builds tab to start a new build.
|
Click on the <i class="fa fa-tasks" style="margin-left: 6px"></i> Builds tab to start a new build.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li ng-if="repository.can_write">
|
<li ng-if="repository.can_write">
|
||||||
<a ng-click="askDeleteMultipleTags(checkedTags.checked)"
|
<a ng-click="askDeleteMultipleTags(checkedTags.checked)"
|
||||||
ng-class="repository.trust_enabled ? 'disabled-option' : ''">
|
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||||
<i class="fa fa-times"></i><span class="text">Delete Tags</span>
|
<i class="fa fa-times"></i><span class="text">Delete Tags</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -243,7 +243,7 @@
|
||||||
<span bo-if="repository.can_write">
|
<span bo-if="repository.can_write">
|
||||||
<span class="cor-options-menu">
|
<span class="cor-options-menu">
|
||||||
<span class="cor-option" option-click="askAddTag(tag)"
|
<span class="cor-option" option-click="askAddTag(tag)"
|
||||||
ng-class="repository.trust_enabled ? 'disabled-option' : ''">
|
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||||
<i class="fa fa-plus"></i> Add New Tag
|
<i class="fa fa-plus"></i> Add New Tag
|
||||||
</span>
|
</span>
|
||||||
<span class="cor-option" option-click="showLabelEditor(tag)"
|
<span class="cor-option" option-click="showLabelEditor(tag)"
|
||||||
|
@ -251,7 +251,7 @@
|
||||||
<i class="fa fa-tags"></i> Edit Labels
|
<i class="fa fa-tags"></i> Edit Labels
|
||||||
</span>
|
</span>
|
||||||
<span class="cor-option" option-click="askDeleteTag(tag.name)"
|
<span class="cor-option" option-click="askDeleteTag(tag.name)"
|
||||||
ng-class="repository.trust_enabled ? 'disabled-option' : ''">
|
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
|
||||||
<i class="fa fa-times"></i> Delete Tag
|
<i class="fa fa-times"></i> Delete Tag
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -144,16 +144,17 @@
|
||||||
manifest-digest="restoreTagInfo.manifest_digest"></span>?
|
manifest-digest="restoreTagInfo.manifest_digest"></span>?
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Trust Enabled Dialog -->
|
<!-- Tag Operations Disabled Dialog -->
|
||||||
<div class="modal fade" id="trustEnabledModal">
|
<div class="modal fade" id="tagOperationsDisabledModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
Cannot execute with trust enabled
|
Tag operations have been disabled.
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
The selected operation cannot be performed on this repository because Quay Trust is
|
The selected operation cannot be performed on this repository because tag operations have been disabled
|
||||||
enabled, which requires that all operations be signed by a user.
|
by an administrator. <span ng-if="repository.trust_enabled">Trust is enabled for this repo, so any tag changes
|
||||||
|
should be performed by users with signing keys.</span>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /.modal-content -->
|
</div><!-- /.modal-content -->
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
|
|
|
@ -18,7 +18,11 @@
|
||||||
Signing is enabled on this repository and all tag operations must be signed via Docker Content Trust.
|
Signing is enabled on this repository and all tag operations must be signed via Docker Content Trust.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Note that due to this feature being enabled, all UI-based tag operations and all build support is <strong>disabled on this repository</strong>.
|
When this feature is enabled, it will be possible to use the UI or client tools to change tag data without
|
||||||
|
signing.
|
||||||
|
This can make a signed tag point to a different image than the actual tag, and the underlying data could
|
||||||
|
be garbage collected. It is important to have a strict separation between tags that are signed and tags
|
||||||
|
that are not.
|
||||||
</p>
|
</p>
|
||||||
<button class="btn btn-danger" ng-click="$ctrl.askChangeTrust(false)">Disable Trust</button>
|
<button class="btn btn-danger" ng-click="$ctrl.askChangeTrust(false)">Disable Trust</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,12 +47,14 @@
|
||||||
dialog-title="Enable Trust"
|
dialog-title="Enable Trust"
|
||||||
dialog-action-title="Enable Trust">
|
dialog-action-title="Enable Trust">
|
||||||
<p>Click "Enable Trust" to enable content trust on this repository.</p>
|
<p>Click "Enable Trust" to enable content trust on this repository.</p>
|
||||||
<p>Please note that at this time, having content trust will <strong>disable</strong> the following
|
<p>Please note that this will not prevent users from overwriting signed tags without updating signatures.
|
||||||
features under the repository:
|
This means that:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Any tag operations in the UI (Add Tag, Delete Tag, Restore Tag)
|
<li>Any tag operations in the UI or client can cause inconsistency
|
||||||
<li>All build triggers and ability to invoke builds
|
<li>Builds should not push to signed tags
|
||||||
</ul>
|
</ul>
|
||||||
|
We recommend you maintain a strict separation between signed and unsigned tags to avoid any issues with garbage
|
||||||
|
collection.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,9 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.alertOnTrust = function() {
|
$scope.alertOnTagOpsDisabled = function() {
|
||||||
if ($scope.repository.trust_enabled) {
|
if ($scope.repository.tag_operations_disabled) {
|
||||||
$('#trustEnabledModal').modal('show');
|
$('#tagOperationsDisabledModal').modal('show');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
|
|
||||||
$scope.createOrMoveTag = function(image, tag) {
|
$scope.createOrMoveTag = function(image, tag) {
|
||||||
if (!$scope.repository.can_write) { return; }
|
if (!$scope.repository.can_write) { return; }
|
||||||
if ($scope.alertOnTrust()) {
|
if ($scope.alertOnTagOpsDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
|
|
||||||
$scope.actionHandler = {
|
$scope.actionHandler = {
|
||||||
'askDeleteTag': function(tag) {
|
'askDeleteTag': function(tag) {
|
||||||
if ($scope.alertOnTrust()) {
|
if ($scope.alertOnTagOpsDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
'askDeleteMultipleTags': function(tags) {
|
'askDeleteMultipleTags': function(tags) {
|
||||||
if ($scope.alertOnTrust()) {
|
if ($scope.alertOnTagOpsDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
'askAddTag': function(image) {
|
'askAddTag': function(image) {
|
||||||
if ($scope.alertOnTrust()) {
|
if ($scope.alertOnTagOpsDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ angular.module('quay').directive('tagOperationsDialog', function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
'askRestoreTag': function(tag, image_id, opt_manifest_digest) {
|
'askRestoreTag': function(tag, image_id, opt_manifest_digest) {
|
||||||
if ($scope.alertOnTrust()) {
|
if ($scope.alertOnTagOpsDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ export type Repository = {
|
||||||
kind?: string;
|
kind?: string;
|
||||||
namespace?: string;
|
namespace?: string;
|
||||||
trust_enabled?: boolean;
|
trust_enabled?: boolean;
|
||||||
|
tag_operations_disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in a new issue