This patch implements the database model required for the network RBAC work. In addition it migrates the current network and subnet 'shared' attributes to leverage the new table. 'shared' is no longer a property of the DB model because its status is based on the tenant ID of the API caller. From an API perspective this is the same (tenants will see networks as 'shared=True' if the network is shared with them). However, internal callers (e.g. plugins, drivers, services) will not be able to check for the 'shared' attribute on network and subnet db objects any more. This patch just achieves parity with the current shared behavior so it doesn't add the ability to manipulate the RBAC entries directly. The RBAC API is in the following patch. Partially-Implements: blueprint rbac-networks Change-Id: I3426b13eede8bfa29729cf3efea3419fb91175c4changes/07/191707/22
parent
eb9226214f
commit
3e0328b992
@ -1,3 +1,3 @@
|
||||
30018084ec99
|
||||
313373c0ffee
|
||||
4ffceebfada
|
||||
8675309a5c4f
|
||||
kilo
|
||||
|
@ -0,0 +1,69 @@
|
||||
# Copyright 2015 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.
|
||||
#
|
||||
|
||||
"""network_rbac
|
||||
|
||||
Revision ID: 4ffceebfada
|
||||
Revises: 30018084ec99
|
||||
Create Date: 2015-06-14 13:12:04.012457
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4ffceebfada'
|
||||
down_revision = '30018084ec99'
|
||||
depends_on = ('8675309a5c4f',)
|
||||
|
||||
from alembic import op
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# A simple model of the networks table with only the fields needed for
|
||||
# the migration.
|
||||
network = sa.Table('networks', sa.MetaData(),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=255)),
|
||||
sa.Column('shared', sa.Boolean(), nullable=False))
|
||||
|
||||
networkrbacs = sa.Table(
|
||||
'networkrbacs', sa.MetaData(),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('object_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=255), nullable=True,
|
||||
index=True),
|
||||
sa.Column('target_tenant', sa.String(length=255), nullable=False),
|
||||
sa.Column('action', sa.String(length=255), nullable=False))
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.bulk_insert(networkrbacs, get_values())
|
||||
op.drop_column('networks', 'shared')
|
||||
# the shared column on subnets was just an internal representation of the
|
||||
# shared status of the network it was related to. This is now handled by
|
||||
# other logic so we just drop it.
|
||||
op.drop_column('subnets', 'shared')
|
||||
|
||||
|
||||
def get_values():
|
||||
session = sa.orm.Session(bind=op.get_bind())
|
||||
values = []
|
||||
for row in session.query(network).filter(network.c.shared).all():
|
||||
values.append({'id': uuidutils.generate_uuid(), 'object_id': row[0],
|
||||
'tenant_id': row[1], 'target_tenant': '*',
|
||||
'action': 'access_as_shared'})
|
||||
# this commit appears to be necessary to allow further operations
|
||||
session.commit()
|
||||
return values
|
@ -0,0 +1,47 @@
|
||||
# Copyright 2015 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.
|
||||
#
|
||||
|
||||
"""network_rbac
|
||||
|
||||
Revision ID: 8675309a5c4f
|
||||
Revises: 313373c0ffee
|
||||
Create Date: 2015-06-14 13:12:04.012457
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8675309a5c4f'
|
||||
down_revision = '313373c0ffee'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'networkrbacs',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('object_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=255), nullable=True,
|
||||
index=True),
|
||||
sa.Column('target_tenant', sa.String(length=255), nullable=False),
|
||||
sa.Column('action', sa.String(length=255), nullable=False),
|
||||
sa.ForeignKeyConstraint(['object_id'],
|
||||
['networks.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint(
|
||||
'action', 'object_id', 'target_tenant',
|
||||
name='uniq_networkrbacs0tenant_target0object_id0action'))
|
@ -0,0 +1,85 @@
|
||||
# Copyright (c) 2015 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import validates
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
|
||||
|
||||
class InvalidActionForType(n_exc.InvalidInput):
|
||||
message = _("Invalid action '%(action)s' for object type "
|
||||
"'%(object_type)s'. Valid actions: %(valid_actions)s")
|
||||
|
||||
|
||||
class RBACColumns(models_v2.HasId, models_v2.HasTenant):
|
||||
"""Mixin that object-specific RBAC tables should inherit.
|
||||
|
||||
All RBAC tables should inherit directly from this one because
|
||||
the RBAC code uses the __subclasses__() method to discover the
|
||||
RBAC types.
|
||||
"""
|
||||
|
||||
# the target_tenant is the subject that the policy will affect. this may
|
||||
# also be a wildcard '*' to indicate all tenants or it may be a role if
|
||||
# neutron gets better integration with keystone
|
||||
target_tenant = sa.Column(sa.String(255), nullable=False)
|
||||
|
||||
action = sa.Column(sa.String(255), nullable=False)
|
||||
|
||||
@abc.abstractproperty
|
||||
def object_type(self):
|
||||
# this determines the name that users will use in the API
|
||||
# to reference the type. sub-classes should set their own
|
||||
pass
|
||||
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint('target_tenant', 'object_id', 'action'),
|
||||
model_base.BASEV2.__table_args__
|
||||
)
|
||||
|
||||
@validates('action')
|
||||
def _validate_action(self, key, action):
|
||||
if action not in self.get_valid_actions():
|
||||
raise InvalidActionForType(
|
||||
action=action, object_type=self.object_type,
|
||||
valid_actions=self.get_valid_actions())
|
||||
return action
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_valid_actions(self):
|
||||
# object table needs to override this to return an interable
|
||||
# with the valid actions rbac entries
|
||||
pass
|
||||
|
||||
|
||||
def get_type_model_map():
|
||||
return {table.object_type: table for table in RBACColumns.__subclasses__()}
|
||||
|
||||
|
||||
class NetworkRBAC(RBACColumns, model_base.BASEV2):
|
||||
"""RBAC table for networks."""
|
||||
|
||||
object_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('networks.id', ondelete="CASCADE"),
|
||||
nullable=False)
|
||||
object_type = 'network'
|
||||
|
||||
def get_valid_actions(self):
|
||||
return ('access_as_shared',)
|
Loading…
Reference in new issue