ae5bad49cc
Related-Blueprint: neutron-lib Change-Id: Ia014468bd621c4ee6aea95bf19328c61070174c4
137 lines
6.0 KiB
Python
137 lines
6.0 KiB
Python
# 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.
|
|
|
|
from neutron_lib import exceptions as n_exc
|
|
from oslo_db import exception as db_exc
|
|
from sqlalchemy.orm import exc
|
|
|
|
from neutron.callbacks import events
|
|
from neutron.callbacks import exceptions as c_exc
|
|
from neutron.callbacks import registry
|
|
from neutron.db import common_db_mixin
|
|
from neutron.db import rbac_db_models as models
|
|
from neutron.extensions import rbac as ext_rbac
|
|
|
|
# resource name using in callbacks
|
|
RBAC_POLICY = 'rbac-policy'
|
|
|
|
|
|
class RbacPluginMixin(common_db_mixin.CommonDbMixin):
|
|
"""Plugin mixin that implements the RBAC DB operations."""
|
|
|
|
object_type_cache = {}
|
|
supported_extension_aliases = ['rbac-policies']
|
|
|
|
def create_rbac_policy(self, context, rbac_policy):
|
|
e = rbac_policy['rbac_policy']
|
|
try:
|
|
registry.notify(RBAC_POLICY, events.BEFORE_CREATE, self,
|
|
context=context, object_type=e['object_type'],
|
|
policy=e)
|
|
except c_exc.CallbackFailure as e:
|
|
raise n_exc.InvalidInput(error_message=e)
|
|
dbmodel = models.get_type_model_map()[e['object_type']]
|
|
try:
|
|
with context.session.begin(subtransactions=True):
|
|
db_entry = dbmodel(object_id=e['object_id'],
|
|
target_tenant=e['target_tenant'],
|
|
action=e['action'],
|
|
tenant_id=e['tenant_id'])
|
|
context.session.add(db_entry)
|
|
except db_exc.DBDuplicateEntry:
|
|
raise ext_rbac.DuplicateRbacPolicy()
|
|
return self._make_rbac_policy_dict(db_entry)
|
|
|
|
def _make_rbac_policy_dict(self, db_entry, fields=None):
|
|
res = {f: db_entry[f] for f in ('id', 'tenant_id', 'target_tenant',
|
|
'action', 'object_id')}
|
|
res['object_type'] = db_entry.object_type
|
|
return self._fields(res, fields)
|
|
|
|
def update_rbac_policy(self, context, id, rbac_policy):
|
|
pol = rbac_policy['rbac_policy']
|
|
entry = self._get_rbac_policy(context, id)
|
|
object_type = entry['object_type']
|
|
try:
|
|
registry.notify(RBAC_POLICY, events.BEFORE_UPDATE, self,
|
|
context=context, policy=entry,
|
|
object_type=object_type, policy_update=pol)
|
|
except c_exc.CallbackFailure as ex:
|
|
raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'],
|
|
details=ex)
|
|
with context.session.begin(subtransactions=True):
|
|
entry.update(pol)
|
|
return self._make_rbac_policy_dict(entry)
|
|
|
|
def delete_rbac_policy(self, context, id):
|
|
entry = self._get_rbac_policy(context, id)
|
|
object_type = entry['object_type']
|
|
try:
|
|
registry.notify(RBAC_POLICY, events.BEFORE_DELETE, self,
|
|
context=context, object_type=object_type,
|
|
policy=entry)
|
|
except c_exc.CallbackFailure as ex:
|
|
raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'],
|
|
details=ex)
|
|
with context.session.begin(subtransactions=True):
|
|
context.session.delete(entry)
|
|
self.object_type_cache.pop(id, None)
|
|
|
|
def _get_rbac_policy(self, context, id):
|
|
object_type = self._get_object_type(context, id)
|
|
dbmodel = models.get_type_model_map()[object_type]
|
|
try:
|
|
return self._model_query(context,
|
|
dbmodel).filter(dbmodel.id == id).one()
|
|
except exc.NoResultFound:
|
|
raise ext_rbac.RbacPolicyNotFound(id=id, object_type=object_type)
|
|
|
|
def get_rbac_policy(self, context, id, fields=None):
|
|
return self._make_rbac_policy_dict(
|
|
self._get_rbac_policy(context, id), fields=fields)
|
|
|
|
def get_rbac_policies(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, page_reverse=False):
|
|
filters = filters or {}
|
|
object_type_filters = filters.pop('object_type', None)
|
|
models_to_query = [
|
|
m for t, m in models.get_type_model_map().items()
|
|
if object_type_filters is None or t in object_type_filters
|
|
]
|
|
collections = [self._get_collection(
|
|
context, model, self._make_rbac_policy_dict,
|
|
filters=filters, fields=fields, sorts=sorts,
|
|
limit=limit, page_reverse=page_reverse)
|
|
for model in models_to_query]
|
|
# NOTE(kevinbenton): we don't have to worry about pagination,
|
|
# limits, or page_reverse currently because allow_pagination is
|
|
# set to False in 'neutron.extensions.rbac'
|
|
return [item for c in collections for item in c]
|
|
|
|
def _get_object_type(self, context, entry_id):
|
|
"""Scans all RBAC tables for an ID to figure out the type.
|
|
|
|
This will be an expensive operation as the number of RBAC tables grows.
|
|
The result is cached since object types cannot be updated for a policy.
|
|
"""
|
|
if entry_id in self.object_type_cache:
|
|
return self.object_type_cache[entry_id]
|
|
for otype, model in models.get_type_model_map().items():
|
|
if (context.session.query(model).
|
|
filter(model.id == entry_id).first()):
|
|
self.object_type_cache[entry_id] = otype
|
|
return otype
|
|
raise ext_rbac.RbacPolicyNotFound(id=entry_id, object_type='unknown')
|