1 # Copyright (c) 2015 Mirantis, Inc.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
16 from sqlalchemy.orm import exc
18 from neutron.callbacks import events
19 from neutron.callbacks import exceptions as c_exc
20 from neutron.callbacks import registry
21 from neutron.common import exceptions as n_exc
22 from neutron.db import common_db_mixin
23 from neutron.db import rbac_db_models as models
24 from neutron.extensions import rbac as ext_rbac
26 # resource name using in callbacks
27 RBAC_POLICY = 'rbac-policy'
30 class RbacPluginMixin(common_db_mixin.CommonDbMixin):
31 """Plugin mixin that implements the RBAC DB operations."""
33 object_type_cache = {}
34 supported_extension_aliases = ['rbac-policies']
36 def create_rbac_policy(self, context, rbac_policy):
37 e = rbac_policy['rbac_policy']
39 registry.notify(RBAC_POLICY, events.BEFORE_CREATE, self,
40 context=context, object_type=e['object_type'],
42 except c_exc.CallbackFailure as e:
43 raise n_exc.InvalidInput(error_message=e)
44 dbmodel = models.get_type_model_map()[e['object_type']]
45 with context.session.begin(subtransactions=True):
46 db_entry = dbmodel(object_id=e['object_id'],
47 target_tenant=e['target_tenant'],
49 tenant_id=e['tenant_id'])
50 context.session.add(db_entry)
51 return self._make_rbac_policy_dict(db_entry)
53 def _make_rbac_policy_dict(self, db_entry, fields=None):
54 res = {f: db_entry[f] for f in ('id', 'tenant_id', 'target_tenant',
55 'action', 'object_id')}
56 res['object_type'] = db_entry.object_type
57 return self._fields(res, fields)
59 def update_rbac_policy(self, context, id, rbac_policy):
60 pol = rbac_policy['rbac_policy']
61 entry = self._get_rbac_policy(context, id)
62 object_type = entry['object_type']
64 registry.notify(RBAC_POLICY, events.BEFORE_UPDATE, self,
65 context=context, policy=entry,
66 object_type=object_type, policy_update=pol)
67 except c_exc.CallbackFailure as ex:
68 raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'],
70 with context.session.begin(subtransactions=True):
72 return self._make_rbac_policy_dict(entry)
74 def delete_rbac_policy(self, context, id):
75 entry = self._get_rbac_policy(context, id)
76 object_type = entry['object_type']
78 registry.notify(RBAC_POLICY, events.BEFORE_DELETE, self,
79 context=context, object_type=object_type,
81 except c_exc.CallbackFailure as ex:
82 raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'],
84 with context.session.begin(subtransactions=True):
85 context.session.delete(entry)
86 self.object_type_cache.pop(id, None)
88 def _get_rbac_policy(self, context, id):
89 object_type = self._get_object_type(context, id)
90 dbmodel = models.get_type_model_map()[object_type]
92 return self._model_query(context,
93 dbmodel).filter(dbmodel.id == id).one()
94 except exc.NoResultFound:
95 raise ext_rbac.RbacPolicyNotFound(id=id, object_type=object_type)
97 def get_rbac_policy(self, context, id, fields=None):
98 return self._make_rbac_policy_dict(
99 self._get_rbac_policy(context, id), fields=fields)
101 def get_rbac_policies(self, context, filters=None, fields=None,
102 sorts=None, limit=None, page_reverse=False):
103 model = common_db_mixin.UnionModel(
104 models.get_type_model_map(), 'object_type')
105 return self._get_collection(
106 context, model, self._make_rbac_policy_dict, filters=filters,
107 fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse)
109 def _get_object_type(self, context, entry_id):
110 """Scans all RBAC tables for an ID to figure out the type.
112 This will be an expensive operation as the number of RBAC tables grows.
113 The result is cached since object types cannot be updated for a policy.
115 if entry_id in self.object_type_cache:
116 return self.object_type_cache[entry_id]
117 for otype, model in models.get_type_model_map().items():
118 if (context.session.query(model).
119 filter(model.id == entry_id).first()):
120 self.object_type_cache[entry_id] = otype
122 raise ext_rbac.RbacPolicyNotFound(id=entry_id, object_type='unknown')