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:
parent
2088caf04b
commit
592cf2ec28
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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'),)
|
||||
|
@ -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
|
||||
|
@ -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. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user