Add support for modifying Generic Containers

This adds POST and DELETE support for a 'secrets' sub-resource on the
containers resource. This will allow a user to add or remove secret
references to an existing container. Only generic containers are
supported per the blueprint for this feature.

If a secret reference already exists in the container, an appropriate
error will be returned indicating that the secret already exists in
the container. I chose this approach over silently accepting the
update, as the name for the container secret could change, so a delete
and re-add seems to be the safer option if a user just wants to change
the name, which is not something that I see happening too often.
Additionally, the action is a POST and not a PUT, so it shouldn't
really update an existing resource.

APIImpact
Add support for POST and DELETE on container secrets sub-resources

DocImpact
Implements: blueprint api-containers-add-put

Change-Id: I6dfa6715385f421e4f173cf73c2b75b68da67051
This commit is contained in:
John McKenzie 2016-06-08 11:29:49 -05:00
parent 2088caf04b
commit 592cf2ec28
7 changed files with 823 additions and 4 deletions

View File

@ -122,6 +122,9 @@ class ContainersController(controllers.ACLMixin):
if not container:
container_not_found()
if len(remainder) == 1 and remainder[0] == 'secrets':
return ContainersSecretsController(container), ()
return ContainerController(container), remainder
@pecan.expose(generic=True, template='json')
@ -213,3 +216,107 @@ class ContainersController(controllers.ACLMixin):
external_project_id)
return {'container_ref': url}
class ContainersSecretsController(controllers.ACLMixin):
"""Handles ContainerSecret creation and deletion requests."""
def __init__(self, container):
LOG.debug('=== Creating ContainerSecretsController ===')
self.container = container
self.container_secret_repo = repo.get_container_secret_repository()
self.secret_repo = repo.get_secret_repository()
self.validator = validators.ContainerSecretValidator()
@pecan.expose(generic=True)
def index(self, **kwargs):
pecan.abort(405) # HTTP 405 Method Not Allowed as default
@index.when(method='POST', template='json')
@controllers.handle_exceptions(u._('Container Secret creation'))
@controllers.enforce_rbac('container_secret:post')
@controllers.enforce_content_types(['application/json'])
def on_post(self, external_project_id, **kwargs):
"""Handles adding an existing secret to an existing container."""
if self.container.type != 'generic':
pecan.abort(400, u._("Only 'generic' containers can be modified."))
data = api.load_body(pecan.request, validator=self.validator)
name = data.get('name')
secret_ref = data.get('secret_ref')
secret_id = hrefs.get_secret_id_from_ref(secret_ref)
secret = self.secret_repo.get(
entity_id=secret_id,
external_project_id=external_project_id,
suppress_exception=True)
if not secret:
pecan.abort(404, u._("Secret provided doesn't exist."))
found_container_secrets = list(
filter(lambda cs: cs.secret_id == secret_id and cs.name == name,
self.container.container_secrets)
)
if found_container_secrets:
pecan.abort(409, u._('Conflict. A secret with that name and ID is '
'already stored in this container. The same '
'secret can exist in a container as long as '
'the name is unique.'))
LOG.debug('Start container secret on_post...%s', secret_ref)
new_container_secret = models.ContainerSecret()
new_container_secret.container_id = self.container.id
new_container_secret.name = name
new_container_secret.secret_id = secret_id
self.container_secret_repo.save(new_container_secret)
url = hrefs.convert_container_to_href(self.container.id)
LOG.debug('URI to container is %s', url)
pecan.response.status = 201
pecan.response.headers['Location'] = url
LOG.info(u._LI('Created a container secret for project: %s'),
external_project_id)
return {'container_ref': url}
@index.when(method='DELETE')
@utils.allow_all_content_types
@controllers.handle_exceptions(u._('Container Secret deletion'))
@controllers.enforce_rbac('container_secret:delete')
def on_delete(self, external_project_id, **kwargs):
"""Handles removing a secret reference from an existing container."""
data = api.load_body(pecan.request, validator=self.validator)
name = data.get('name')
secret_ref = data.get('secret_ref')
secret_id = hrefs.get_secret_id_from_ref(secret_ref)
secret = self.secret_repo.get(
entity_id=secret_id,
external_project_id=external_project_id,
suppress_exception=True)
if not secret:
pecan.abort(404, u._("Secret '{secret_name}' with reference "
"'{secret_ref}' doesn't exist.").format(
secret_name=name, secret_ref=secret_ref))
found_container_secrets = list(
filter(lambda cs: cs.secret_id == secret_id and cs.name == name,
self.container.container_secrets)
)
if not found_container_secrets:
pecan.abort(404, u._('Secret provided is not in the container'))
for container_secret in found_container_secrets:
self.container_secret_repo.delete_entity_by_id(
container_secret.id, external_project_id)
pecan.response.status = 204
LOG.info(u._LI('Deleted container secret for project: %s'),
external_project_id)

View File

@ -150,11 +150,11 @@ def get_container_id_from_ref(container_ref):
def get_secret_id_from_ref(secret_ref):
"""Parse a secret reference and return the secret ID
TODO(Dave) Implement this, or make one generic ID from REF function
:param secret_ref: HTTP reference of secret
:return: a string containing the ID of the secret
"""
secret_id = secret_ref.rsplit('/', 1)[1]
return secret_id
def get_ca_id_from_ref(ca_ref):

View File

@ -775,6 +775,27 @@ class ContainerConsumerValidator(ValidatorBase):
return json_data
class ContainerSecretValidator(ValidatorBase):
"""Validate a Container Secret."""
def __init__(self):
self.name = 'ContainerSecret'
self.schema = {
"type": "object",
"properties": {
"name": {"type": "string", "maxLength": 255},
"secret_ref": {"type": "string", "minLength": 1}
},
"required": ["secret_ref"]
}
def validate(self, json_data, parent_schema=None):
schema_name = self._full_name(parent_schema)
self._assert_schema_is_valid(json_data, schema_name)
return json_data
class ContainerValidator(ValidatorBase):
"""Validator for all types of Container."""

View File

@ -229,9 +229,15 @@ class ContainerSecret(BASE, SoftDeleteMixIn, ModelBase):
# Eager load this relationship via 'lazy=False'.
container = orm.relationship(
'Container', backref=orm.backref('container_secrets', lazy=False))
'Container',
backref=orm.backref('container_secrets', lazy=False,
primaryjoin="and_(ContainerSecret.container_id == "
"Container.id, ContainerSecret.deleted!=1)"))
secrets = orm.relationship(
'Secret', backref=orm.backref('container_secrets'))
'Secret',
backref=orm.backref('container_secrets',
primaryjoin="and_(ContainerSecret.secret_id == "
"Secret.id, ContainerSecret.deleted!=1)"))
__table_args__ = (sa.UniqueConstraint('container_id', 'secret_id', 'name',
name='_container_secret_name_uc'),)

View File

@ -269,6 +269,484 @@ class WhenGettingOrDeletingContainerUsingContainerResource(
self.assertEqual("application/json", resp.content_type)
class WhenAddingOrRemovingContainerSecretsUsingContainersSecretsResource(
utils.BarbicanAPIBaseTestCase,
SuccessfulContainerCreateMixin):
def test_should_add_container_secret(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
def test_should_add_container_secret_without_name(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
def test_should_add_container_secret_with_different_name(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
secret_name = 'test secret 2'
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(2, len(container.container_secrets))
def test_should_not_add_when_secret_not_found(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_ref = '/secrets/bad_id'
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
expect_errors=True
)
self.assertEqual(404, resp.status_int)
def test_should_not_add_container_secret_with_invalid_name(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
container_secret_name = "x" * 256
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=container_secret_name,
expect_errors=True
)
self.assertEqual(400, resp.status_int)
def test_should_not_add_container_secret_with_invalid_secret_ref(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_ref = ""
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
expect_errors=True
)
self.assertEqual(400, resp.status_int)
def test_should_add_different_secret_refs_with_duplicate_name(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
first_secret_ref = resp.json.get('secret_ref')
secret_name = 'test secret 2'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
second_secret_ref = resp.json.get('secret_ref')
container_secret_name = 'test container secret name'
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=first_secret_ref,
name=container_secret_name
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=second_secret_ref,
name=container_secret_name
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(2, len(container.container_secrets))
def test_should_not_allow_add_on_rsa_container(self):
container_name = 'test container name'
container_type = 'rsa'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name,
expect_errors=True
)
self.assertEqual(400, resp.status_int)
def test_should_not_allow_add_on_certificate_container(self):
container_name = 'test container name'
container_type = 'certificate'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name,
expect_errors=True
)
self.assertEqual(400, resp.status_int)
def test_should_not_allow_add_secret_when_exists_in_container(self):
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name
)
self._assert_successful_container_create(resp, container_uuid)
resp, updated_container_uuid = create_container_secret(
self.app,
container_id=container_uuid,
secret_ref=secret_ref,
name=secret_name,
expect_errors=True
)
self.assertEqual(409, resp.status_int)
def test_should_delete_existing_container_secret(self):
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
container_name = 'test container name'
container_type = 'generic'
secret_refs = [
{
'name': secret_name,
'secret_ref': secret_ref
}
]
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type,
secret_refs=secret_refs
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
resp = delete_container_secret(self.app, container_uuid, secret_ref,
secret_name)
self.assertEqual(204, resp.status_int)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
def test_should_delete_container_secret_without_name(self):
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
container_name = 'test container name'
container_type = 'generic'
secret_refs = [
{
'secret_ref': secret_ref
}
]
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type,
secret_refs=secret_refs
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
resp = delete_container_secret(self.app, container_uuid, secret_ref)
self.assertEqual(204, resp.status_int)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(0, len(container.container_secrets))
def test_should_not_delete_container_secret_with_incorrect_name(self):
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
container_name = 'test container name'
container_type = 'generic'
secret_refs = [
{
'name': secret_name,
'secret_ref': secret_ref
}
]
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type,
secret_refs=secret_refs
)
self._assert_successful_container_create(resp, container_uuid)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
incorrect_name = 'test incorrect name'
resp = delete_container_secret(self.app, container_uuid, secret_ref,
incorrect_name, expect_errors=True)
self.assertEqual(404, resp.status_int)
container = containers_repo.get(container_uuid, self.project_id)
self.assertEqual(1, len(container.container_secrets))
def test_should_delete_only_when_secret_exists(self):
secret_ref = '/secrets/bad_id'
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
resp = delete_container_secret(self.app, container_uuid, secret_ref,
expect_errors=True)
self.assertEqual(404, resp.status_int)
def test_should_delete_only_when_secret_exists_in_container(self):
secret_name = 'test secret 1'
resp, _ = secret_helper.create_secret(
self.app,
name=secret_name
)
self.assertEqual(201, resp.status_int)
secret_ref = resp.json.get('secret_ref')
container_name = 'test container name'
container_type = 'generic'
resp, container_uuid = create_container(
self.app,
name=container_name,
container_type=container_type
)
self._assert_successful_container_create(resp, container_uuid)
resp = delete_container_secret(self.app, container_uuid, secret_ref,
secret_name, expect_errors=True)
self.assertEqual(404, resp.status_int)
class WhenPerformingUnallowedOperationsOnContainers(
utils.BarbicanAPIBaseTestCase,
SuccessfulContainerCreateMixin):
@ -281,6 +759,11 @@ class WhenPerformingUnallowedOperationsOnContainers(
}
]
secret_req = {
'name': 'test secret name',
'secret_ref': 'https://localhost/v1/secrets/1-2-3-4'
}
def test_should_not_allow_put_on_containers(self):
resp = self.app.put_json(
'/containers/',
@ -319,6 +802,37 @@ class WhenPerformingUnallowedOperationsOnContainers(
)
self.assertEqual(405, resp.status_int)
def test_should_not_allow_get_on_container_secrets(self):
resp, container_uuid = create_container(
self.app,
name='test container name',
container_type='generic'
)
self._assert_successful_container_create(resp, container_uuid)
resp = self.app.get(
'/containers/{container_id}/secrets'.format(
container_id=container_uuid),
expect_errors=True
)
self.assertEqual(405, resp.status_int)
def test_should_not_allow_put_on_container_secrets(self):
resp, container_uuid = create_container(
self.app,
name='test container name',
container_type='generic'
)
self._assert_successful_container_create(resp, container_uuid)
resp = self.app.put_json(
'/containers/{container_id}/secrets'.format(
container_id=container_uuid),
self.secret_req,
expect_errors=True
)
self.assertEqual(405, resp.status_int)
# ----------------------- Helper Functions ---------------------------
def create_container(app, name=None, container_type=None, secret_refs=None,
@ -344,3 +858,47 @@ def create_container(app, name=None, container_type=None, secret_refs=None,
_, created_uuid = os.path.split(container_ref)
return resp, created_uuid
def create_container_secret(app, container_id=None, secret_ref=None, name=None,
expect_errors=False, headers=None):
request = {
'name': name,
'secret_ref': secret_ref
}
cleaned_request = {key: val for key, val in request.items()
if val is not None}
resp = app.post_json(
'/containers/{container_id}/secrets'.format(container_id=container_id),
cleaned_request,
expect_errors=expect_errors,
headers=headers
)
updated_uuid = None
if resp.status_int == 201:
container_ref = resp.json.get('container_ref', '')
_, updated_uuid = os.path.split(container_ref)
return resp, updated_uuid
def delete_container_secret(app, container_id=None, secret_ref=None, name=None,
expect_errors=False, headers=None):
request = {
'name': name,
'secret_ref': secret_ref
}
cleaned_request = {key: val for key, val in request.items()
if val is not None}
resp = app.delete_json(
'/containers/{container_id}/secrets'.format(container_id=container_id),
cleaned_request,
expect_errors=expect_errors,
headers=headers
)
return resp

View File

@ -276,3 +276,128 @@ HTTP Status Codes
+------+-----------------------------------------------------------------------------+
| 404 | Container not found or unavailable |
+------+-----------------------------------------------------------------------------+
POST /v1/containers/{container_uuid}/secrets
############################################
Add a secret to an existing container. This is only supported on generic
containers.
Request Attributes
******************
+------------+--------+------------------------------------------------------------+
| Name | Type | Description |
+============+========+============================================================+
| name | string | (optional) Human readable name for identifying your secret |
| | | within the container. |
+------------+--------+------------------------------------------------------------+
| secret_ref | uri | (required) Full URI reference to an existing secret. |
+------------+--------+------------------------------------------------------------+
Request:
********
.. code-block:: javascript
POST /v1/containers/{container_uuid}/secrets
Headers:
X-Project-Id: {project_id}
Content:
{
"name": "private_key",
"secret_ref": "https://{barbican_host}/v1/secrets/{secret_uuid}"
}
Response:
*********
.. code-block:: javascript
{
"container_ref": "https://{barbican_host}/v1/containers/{container_uuid}"
}
Note that the requesting 'container_uuid' is the same as that provided in the
response.
HTTP Status Codes
*****************
In general, error codes produced by the containers POST call pertain here as
well, especially in regards to the secret references that can be provided.
+------+-----------------------------------------------------------------------------+
| Code | Description |
+======+=============================================================================+
| 201 | Successful update of the container |
+------+-----------------------------------------------------------------------------+
| 400 | Missing secret_ref |
+------+-----------------------------------------------------------------------------+
| 401 | Invalid X-Auth-Token or the token doesn't have permissions to this resource |
+------+-----------------------------------------------------------------------------+
| 403 | Forbidden. The user has been authenticated, but is not authorized to |
| | add the secret to the specified container. This can be based on the user's |
| | role or the project's quota. |
+------+-----------------------------------------------------------------------------+
DELETE /v1/containers/{container_uuid}/secrets
##############################################
Remove a secret from a container. This is only supported on generic
containers.
Request Attributes
******************
+------------+--------+------------------------------------------------------------+
| Name | Type | Description |
+============+========+============================================================+
| name | string | (optional) Human readable name for identifying your secret |
| | | within the container. |
+------------+--------+------------------------------------------------------------+
| secret_ref | uri | (required) Full URI reference to an existing secret. |
+------------+--------+------------------------------------------------------------+
Request:
********
.. code-block:: javascript
DELETE /v1/containers/{container_uuid}/secrets
Headers:
X-Project-Id: {project_id}
Content:
{
"name": "private key",
"secret_ref": "https://{barbican_host}/v1/secrets/{secret_uuid}"
}
Response:
*********
.. code-block:: javascript
204 No Content
HTTP Status Codes
*****************
+------+-----------------------------------------------------------------------------+
| Code | Description |
+======+=============================================================================+
| 204 | Successful removal of the secret from the container. |
+------+-----------------------------------------------------------------------------+
| 400 | Missing secret_ref |
+------+-----------------------------------------------------------------------------+
| 401 | Invalid X-Auth-Token or the token doesn't have permissions to this resource |
+------+-----------------------------------------------------------------------------+
| 403 | Forbidden. The user has been authenticated, but is not authorized to |
| | remove the secret from the specified container. This can be based on the |
| | user's role or the project's quota. |
+------+-----------------------------------------------------------------------------+
| 404 | Specified secret_ref is not found in the container. |
+------+-----------------------------------------------------------------------------+

View File

@ -46,6 +46,8 @@
"containers:get": "rule:all_but_audit",
"container:get": "rule:container_non_private_read or rule:container_project_creator or rule:container_project_admin or rule:container_acl_read",
"container:delete": "rule:admin",
"container_secret:post": "rule:admin",
"container_secret:delete": "rule:admin",
"transport_key:get": "rule:all_users",
"transport_key:delete": "rule:admin",
"transport_keys:get": "rule:all_users",