From 592cf2ec289acf3c81608e91885f10911c81c323 Mon Sep 17 00:00:00 2001 From: John McKenzie Date: Wed, 8 Jun 2016 11:29:49 -0500 Subject: [PATCH] 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 --- barbican/api/controllers/containers.py | 107 ++++ barbican/common/hrefs.py | 4 +- barbican/common/validators.py | 21 + barbican/model/models.py | 10 +- .../tests/api/controllers/test_containers.py | 558 ++++++++++++++++++ doc/source/api/reference/containers.rst | 125 ++++ etc/barbican/policy.json | 2 + 7 files changed, 823 insertions(+), 4 deletions(-) diff --git a/barbican/api/controllers/containers.py b/barbican/api/controllers/containers.py index 0f7d3342..6ea14dbe 100644 --- a/barbican/api/controllers/containers.py +++ b/barbican/api/controllers/containers.py @@ -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) diff --git a/barbican/common/hrefs.py b/barbican/common/hrefs.py index 95b4a249..80bcc66e 100644 --- a/barbican/common/hrefs.py +++ b/barbican/common/hrefs.py @@ -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): diff --git a/barbican/common/validators.py b/barbican/common/validators.py index eac56841..5dfeb7c5 100644 --- a/barbican/common/validators.py +++ b/barbican/common/validators.py @@ -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.""" diff --git a/barbican/model/models.py b/barbican/model/models.py index ebaa76bf..0afe0c0a 100644 --- a/barbican/model/models.py +++ b/barbican/model/models.py @@ -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'),) diff --git a/barbican/tests/api/controllers/test_containers.py b/barbican/tests/api/controllers/test_containers.py index 4cb803c5..663ce1f4 100644 --- a/barbican/tests/api/controllers/test_containers.py +++ b/barbican/tests/api/controllers/test_containers.py @@ -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 diff --git a/doc/source/api/reference/containers.rst b/doc/source/api/reference/containers.rst index 60e8d949..cbfa8398 100644 --- a/doc/source/api/reference/containers.rst +++ b/doc/source/api/reference/containers.rst @@ -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. | ++------+-----------------------------------------------------------------------------+ diff --git a/etc/barbican/policy.json b/etc/barbican/policy.json index 020cd568..0e51957e 100644 --- a/etc/barbican/policy.json +++ b/etc/barbican/policy.json @@ -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",