8813b0ed2d
This is part of the remaining technical debt of the specs https://specs.openstack.org/openstack/neutron-specs/specs/newton/moving-to-keystone-v3.html Blueprint: https://blueprints.launchpad.net/neutron/+spec/keystone-v3 Change-Id: I2d2fd4d1802c9dfe0778ac8fdddc7b9a8afe7d25
169 lines
7.5 KiB
Python
169 lines
7.5 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.callbacks import events
|
|
from neutron_lib.callbacks import exceptions as c_exc
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib.callbacks import resources
|
|
from neutron_lib.db import api as db_api
|
|
from neutron_lib.db import utils as db_utils
|
|
from neutron_lib import exceptions as n_exc
|
|
from neutron_lib.objects import exceptions as o_exc
|
|
|
|
from neutron.extensions import rbac as ext_rbac
|
|
from neutron.objects import base as base_obj
|
|
from neutron.objects import rbac as rbac_obj
|
|
|
|
|
|
class RbacPluginMixin(object):
|
|
"""Plugin mixin that implements the RBAC DB operations."""
|
|
|
|
object_type_cache = {}
|
|
supported_extension_aliases = ['rbac-policies']
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def create_rbac_policy(self, context, rbac_policy):
|
|
e = rbac_policy['rbac_policy']
|
|
# NOTE(ralonsoh): remove this conversion when "bp/keystone-v3" is
|
|
# widely implemented in all OpenStack projects.
|
|
try:
|
|
e['target_project'] = e.pop('target_tenant')
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
registry.publish(resources.RBAC_POLICY, events.BEFORE_CREATE, self,
|
|
payload=events.DBEventPayload(
|
|
context, request_body=e,
|
|
metadata={'object_type': e['object_type']}))
|
|
except c_exc.CallbackFailure as e:
|
|
raise n_exc.InvalidInput(error_message=e)
|
|
rbac_class = (
|
|
rbac_obj.RBACBaseObject.get_type_class_map()[e['object_type']])
|
|
try:
|
|
rbac_args = {'project_id': e['project_id'],
|
|
'object_id': e['object_id'],
|
|
'action': e['action'],
|
|
'target_project': e['target_project']}
|
|
_rbac_obj = rbac_class(context, **rbac_args)
|
|
_rbac_obj.create()
|
|
except o_exc.NeutronDbObjectDuplicateEntry:
|
|
raise ext_rbac.DuplicateRbacPolicy()
|
|
return self._make_rbac_policy_dict(_rbac_obj)
|
|
|
|
@staticmethod
|
|
def _make_rbac_policy_dict(entry, fields=None):
|
|
res = {f: entry[f] for f in ('id', 'project_id', 'target_project',
|
|
'action', 'object_id')}
|
|
# TODO(ralonsoh): remove once all calls refer to "target_project"
|
|
res['target_tenant'] = res['target_project']
|
|
res['object_type'] = entry.db_model.object_type
|
|
return db_utils.resource_fields(res, fields)
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def update_rbac_policy(self, context, id, rbac_policy):
|
|
pol = rbac_policy['rbac_policy']
|
|
# NOTE(ralonsoh): remove this conversion when "bp/keystone-v3" is
|
|
# widely implemented in all OpenStack projects.
|
|
try:
|
|
pol['target_project'] = pol.pop('target_tenant')
|
|
except KeyError:
|
|
pass
|
|
entry = self._get_rbac_policy(context, id)
|
|
object_type = entry.db_model.object_type
|
|
try:
|
|
registry.publish(resources.RBAC_POLICY, events.BEFORE_UPDATE, self,
|
|
payload=events.DBEventPayload(
|
|
context, request_body=pol,
|
|
states=(entry,), resource_id=id,
|
|
metadata={'object_type': object_type}))
|
|
except c_exc.CallbackFailure as ex:
|
|
raise ext_rbac.RbacPolicyInUse(object_id=entry.object_id,
|
|
details=ex)
|
|
entry.update_fields(pol)
|
|
entry.update()
|
|
return self._make_rbac_policy_dict(entry)
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def delete_rbac_policy(self, context, id):
|
|
entry = self._get_rbac_policy(context, id)
|
|
object_type = entry.db_model.object_type
|
|
try:
|
|
registry.publish(resources.RBAC_POLICY, events.BEFORE_DELETE, self,
|
|
payload=events.DBEventPayload(
|
|
context, states=(entry,), resource_id=id,
|
|
metadata={'object_type': object_type}))
|
|
except c_exc.CallbackFailure as ex:
|
|
raise ext_rbac.RbacPolicyInUse(object_id=entry.object_id,
|
|
details=ex)
|
|
# make a dict copy because deleting the entry will nullify its
|
|
# object_id link to network
|
|
entry_dict = entry.to_dict()
|
|
entry.delete()
|
|
registry.publish(resources.RBAC_POLICY, events.AFTER_DELETE, self,
|
|
payload=events.DBEventPayload(
|
|
context, states=(entry_dict,), resource_id=id,
|
|
metadata={'object_type': object_type}))
|
|
self.object_type_cache.pop(id, None)
|
|
|
|
def _get_rbac_policy(self, context, id):
|
|
object_type = self._get_object_type(context, id)
|
|
rbac_class = rbac_obj.RBACBaseObject.get_type_class_map()[object_type]
|
|
_rbac_obj = rbac_class.get_object(context, id=id)
|
|
if not _rbac_obj:
|
|
raise ext_rbac.RbacPolicyNotFound(id=id, object_type=object_type)
|
|
return _rbac_obj
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def get_rbac_policy(self, context, id, fields=None):
|
|
return self._make_rbac_policy_dict(
|
|
self._get_rbac_policy(context, id), fields=fields)
|
|
|
|
@db_api.retry_if_session_inactive()
|
|
def get_rbac_policies(self, context, filters=None, fields=None,
|
|
sorts=None, limit=None, page_reverse=False):
|
|
pager = base_obj.Pager(sorts, limit, page_reverse)
|
|
filters = filters or {}
|
|
object_types = filters.pop('object_type', None)
|
|
# NOTE(ralonsoh): remove this conversion when "bp/keystone-v3" is
|
|
# widely implemented in all OpenStack projects.
|
|
try:
|
|
filters['target_project'] = filters.pop('target_tenant')
|
|
except KeyError:
|
|
pass
|
|
rbac_classes_to_query = [
|
|
o for t, o in rbac_obj.RBACBaseObject.get_type_class_map().items()
|
|
if not object_types or t in object_types]
|
|
rbac_objs = []
|
|
for rbac_class in rbac_classes_to_query:
|
|
rbac_objs += rbac_class.get_objects(context, _pager=pager,
|
|
**filters)
|
|
return [self._make_rbac_policy_dict(_rbac_obj, fields)
|
|
for _rbac_obj in rbac_objs]
|
|
|
|
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, rbac_class in \
|
|
rbac_obj.RBACBaseObject.get_type_class_map().items():
|
|
if rbac_class.count(context, id=entry_id):
|
|
self.object_type_cache[entry_id] = otype
|
|
return otype
|
|
raise ext_rbac.RbacPolicyNotFound(id=entry_id, object_type='unknown')
|