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()
|
||||
project_children_tables = [models.Order,
|
||||
models.KEKDatum,
|
||||
models.SecretConsumerMetadatum,
|
||||
models.Secret,
|
||||
models.ContainerConsumerMetadatum,
|
||||
models.Container,
|
||||
@ -158,6 +159,8 @@ def cleanup_all(threshold_date=None):
|
||||
total += cleanup_softdeletes(models.ContainerSecret,
|
||||
threshold_date=threshold_date)
|
||||
|
||||
total += cleanup_softdeletes(models.SecretConsumerMetadatum,
|
||||
threshold_date=threshold_date)
|
||||
total += cleanup_parent_with_no_child(models.Secret, models.Order,
|
||||
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",
|
||||
cascade="all, delete-orphan")
|
||||
|
||||
consumers = orm.relationship(
|
||||
"SecretConsumerMetadatum",
|
||||
backref="secret",
|
||||
cascade="all, delete-orphan")
|
||||
|
||||
def __init__(self, parsed_request=None, check_exc=True):
|
||||
"""Creates secret from a dict."""
|
||||
super(Secret, self).__init__()
|
||||
@ -1493,3 +1498,63 @@ class ProjectSecretStore(BASE, ModelBase):
|
||||
"""Sub-class hook method: return dict of fields."""
|
||||
return {'secret_store_id': self.secret_store_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'])
|
||||
|
||||
|
||||
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__':
|
||||
unittest.main()
|
||||
|
Loading…
Reference in New Issue
Block a user