Add SecretConsumerMetadatum model and its tests
This patch is the first of a series to implement the Secret Consumers spec: https://specs.openstack.org/openstack/barbican-specs/specs/train/secret-consumers.html Change-Id: I81155cf7edcc55b80ea4364732f09da798172b74 Signed-off-by: Moises Guimaraes de Medeiros <moguimar@redhat.com>
This commit is contained in:
parent
2a6fc155d2
commit
63e6979023
@ -39,6 +39,7 @@ def cleanup_unassociated_projects():
|
|||||||
session = repo.get_session()
|
session = repo.get_session()
|
||||||
project_children_tables = [models.Order,
|
project_children_tables = [models.Order,
|
||||||
models.KEKDatum,
|
models.KEKDatum,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
models.Secret,
|
models.Secret,
|
||||||
models.ContainerConsumerMetadatum,
|
models.ContainerConsumerMetadatum,
|
||||||
models.Container,
|
models.Container,
|
||||||
@ -158,6 +159,8 @@ def cleanup_all(threshold_date=None):
|
|||||||
total += cleanup_softdeletes(models.ContainerSecret,
|
total += cleanup_softdeletes(models.ContainerSecret,
|
||||||
threshold_date=threshold_date)
|
threshold_date=threshold_date)
|
||||||
|
|
||||||
|
total += cleanup_softdeletes(models.SecretConsumerMetadatum,
|
||||||
|
threshold_date=threshold_date)
|
||||||
total += cleanup_parent_with_no_child(models.Secret, models.Order,
|
total += cleanup_parent_with_no_child(models.Secret, models.Order,
|
||||||
threshold_date=threshold_date)
|
threshold_date=threshold_date)
|
||||||
|
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
# Copyright 2019 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Add Secret Consumers table
|
||||||
|
|
||||||
|
Revision ID: 0f8c192a061f
|
||||||
|
Revises: 39cf2e645cba
|
||||||
|
Create Date: 2019-08-19 12:03:08.567230
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "0f8c192a061f"
|
||||||
|
down_revision = "39cf2e645cba"
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
ctx = op.get_context()
|
||||||
|
con = op.get_bind()
|
||||||
|
table_exists = ctx.dialect.has_table(con.engine,
|
||||||
|
"secret_consumer_metadata")
|
||||||
|
if not table_exists:
|
||||||
|
op.create_table(
|
||||||
|
"secret_consumer_metadata",
|
||||||
|
# ModelBase
|
||||||
|
sa.Column("id", sa.String(length=36), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.Column("deleted_at", sa.DateTime(), nullable=True),
|
||||||
|
sa.Column("deleted", sa.Boolean(), nullable=False),
|
||||||
|
sa.Column("status", sa.String(length=20), nullable=False),
|
||||||
|
# SecretConsumerMetadatum
|
||||||
|
sa.Column("secret_id", sa.String(36), nullable=False),
|
||||||
|
sa.Column("project_id", sa.String(36), nullable=False),
|
||||||
|
sa.Column("service", sa.String(255), nullable=False),
|
||||||
|
sa.Column("resource_type", sa.String(255), nullable=False),
|
||||||
|
sa.Column("resource_id", sa.String(36), nullable=False),
|
||||||
|
# Constraints and Indexes
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
sa.ForeignKeyConstraint(["secret_id"], ["secrets.id"]),
|
||||||
|
sa.UniqueConstraint(
|
||||||
|
"secret_id", "resource_id", name="_secret_consumer_resource_uc"
|
||||||
|
),
|
||||||
|
sa.Index("ix_secret_consumer_metadata_secret_id", "secret_id"),
|
||||||
|
sa.Index("ix_secret_consumer_metadata_resource_id", "resource_id"),
|
||||||
|
)
|
@ -316,6 +316,11 @@ class Secret(BASE, SoftDeleteMixIn, ModelBase):
|
|||||||
backref="secret",
|
backref="secret",
|
||||||
cascade="all, delete-orphan")
|
cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
consumers = orm.relationship(
|
||||||
|
"SecretConsumerMetadatum",
|
||||||
|
backref="secret",
|
||||||
|
cascade="all, delete-orphan")
|
||||||
|
|
||||||
def __init__(self, parsed_request=None, check_exc=True):
|
def __init__(self, parsed_request=None, check_exc=True):
|
||||||
"""Creates secret from a dict."""
|
"""Creates secret from a dict."""
|
||||||
super(Secret, self).__init__()
|
super(Secret, self).__init__()
|
||||||
@ -1493,3 +1498,63 @@ class ProjectSecretStore(BASE, ModelBase):
|
|||||||
"""Sub-class hook method: return dict of fields."""
|
"""Sub-class hook method: return dict of fields."""
|
||||||
return {'secret_store_id': self.secret_store_id,
|
return {'secret_store_id': self.secret_store_id,
|
||||||
'project_id': self.project_id}
|
'project_id': self.project_id}
|
||||||
|
|
||||||
|
|
||||||
|
class SecretConsumerMetadatum(BASE, SoftDeleteMixIn, ModelBase):
|
||||||
|
"""Stores Consumer Registrations for Secrets in the datastore.
|
||||||
|
|
||||||
|
Services can register interest in Secrets. Services will provide a
|
||||||
|
resource type and a resource id for the object that is using the Secret.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "secret_consumer_metadata"
|
||||||
|
|
||||||
|
secret_id = sa.Column(
|
||||||
|
sa.String(36), sa.ForeignKey("secrets.id"), index=True, nullable=False
|
||||||
|
)
|
||||||
|
project_id = sa.Column(
|
||||||
|
sa.String(36), sa.ForeignKey("projects.id"), index=True, nullable=True
|
||||||
|
)
|
||||||
|
service = sa.Column(sa.String(255), nullable=False)
|
||||||
|
resource_type = sa.Column(sa.String(255), nullable=False)
|
||||||
|
resource_id = sa.Column(sa.String(36), index=True, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
sa.UniqueConstraint(
|
||||||
|
"secret_id", "resource_id", name="_secret_consumer_resource_uc"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, secret_id=None, project_id=None, service=None,
|
||||||
|
resource_type=None, resource_id=None, check_exc=True):
|
||||||
|
"""Registers a Consumer to a Secret."""
|
||||||
|
super(SecretConsumerMetadatum, self).__init__()
|
||||||
|
|
||||||
|
msg = u._("Must supply non-None {0} argument "
|
||||||
|
"for SecretConsumerMetadatum entry.")
|
||||||
|
|
||||||
|
if secret_id is None and check_exc:
|
||||||
|
raise exception.MissingArgumentError(msg.format("secret_id"))
|
||||||
|
if project_id is None and check_exc:
|
||||||
|
raise exception.MissingArgumentError(msg.format("project_id"))
|
||||||
|
if service is None and check_exc:
|
||||||
|
raise exception.MissingArgumentError(msg.format("service"))
|
||||||
|
if resource_type is None and check_exc:
|
||||||
|
raise exception.MissingArgumentError(msg.format("resource_type"))
|
||||||
|
if resource_id is None and check_exc:
|
||||||
|
raise exception.MissingArgumentError(msg.format("resource_id"))
|
||||||
|
|
||||||
|
self.secret_id = secret_id
|
||||||
|
self.project_id = project_id
|
||||||
|
self.service = service
|
||||||
|
self.resource_type = resource_type
|
||||||
|
self.resource_id = resource_id
|
||||||
|
self.status = States.ACTIVE
|
||||||
|
|
||||||
|
def _do_extra_dict_fields(self):
|
||||||
|
"""Sub-class hook method: return dict of fields."""
|
||||||
|
return {
|
||||||
|
"service": self.service,
|
||||||
|
"resource_type": self.resource_type,
|
||||||
|
"resource_id": self.resource_id,
|
||||||
|
}
|
||||||
|
@ -748,5 +748,90 @@ class WhenCreatingNewProjectSecretStore(utils.BaseTestCase):
|
|||||||
project_ss.to_dict_fields()['status'])
|
project_ss.to_dict_fields()['status'])
|
||||||
|
|
||||||
|
|
||||||
|
class WhenCreatingNewSecretConsumer(utils.BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(WhenCreatingNewSecretConsumer, self).setUp()
|
||||||
|
self.secret_id = "12345secret"
|
||||||
|
self.project_id = "12345project"
|
||||||
|
self.service = "12345service"
|
||||||
|
self.resource_type = "12345resource_type"
|
||||||
|
self.resource_id = "12345resource_id"
|
||||||
|
|
||||||
|
def test_new_secret_consumer(self):
|
||||||
|
consumer = models.SecretConsumerMetadatum(
|
||||||
|
self.secret_id,
|
||||||
|
self.project_id,
|
||||||
|
self.service,
|
||||||
|
self.resource_type,
|
||||||
|
self.resource_id
|
||||||
|
)
|
||||||
|
self.assertEqual(self.secret_id, consumer.secret_id)
|
||||||
|
self.assertEqual(self.project_id, consumer.project_id)
|
||||||
|
self.assertEqual(self.service, consumer.service)
|
||||||
|
self.assertEqual(self.resource_type, consumer.resource_type)
|
||||||
|
self.assertEqual(self.resource_id, consumer.resource_id)
|
||||||
|
self.assertEqual(models.States.ACTIVE, consumer.status)
|
||||||
|
|
||||||
|
def test_to_dict_fields(self):
|
||||||
|
consumer = models.SecretConsumerMetadatum(
|
||||||
|
self.secret_id,
|
||||||
|
self.project_id,
|
||||||
|
self.service,
|
||||||
|
self.resource_type,
|
||||||
|
self.resource_id
|
||||||
|
)
|
||||||
|
fields = consumer.to_dict_fields()
|
||||||
|
self.assertEqual(self.service, fields["service"])
|
||||||
|
self.assertEqual(self.resource_type, fields["resource_type"])
|
||||||
|
self.assertEqual(self.resource_id, fields["resource_id"])
|
||||||
|
|
||||||
|
def test_should_raise_exception_when_missing_arguments(self):
|
||||||
|
self.assertRaises(
|
||||||
|
exception.MissingArgumentError,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
|
None,
|
||||||
|
self.project_id,
|
||||||
|
self.service,
|
||||||
|
self.resource_type,
|
||||||
|
self.resource_id,
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
exception.MissingArgumentError,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
|
self.secret_id,
|
||||||
|
None,
|
||||||
|
self.service,
|
||||||
|
self.resource_type,
|
||||||
|
self.resource_id,
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
exception.MissingArgumentError,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
|
self.secret_id,
|
||||||
|
self.project_id,
|
||||||
|
None,
|
||||||
|
self.resource_type,
|
||||||
|
self.resource_id,
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
exception.MissingArgumentError,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
|
self.secret_id,
|
||||||
|
self.project_id,
|
||||||
|
self.service,
|
||||||
|
None,
|
||||||
|
self.resource_id,
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
exception.MissingArgumentError,
|
||||||
|
models.SecretConsumerMetadatum,
|
||||||
|
self.secret_id,
|
||||||
|
self.project_id,
|
||||||
|
self.service,
|
||||||
|
self.resource_type,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user