f2efb6465edae57043a947cddcc6943dafc04070
[openstack-build/neutron-build.git] / neutron / db / rbac_db_mixin.py
1 # Copyright (c) 2015 Mirantis, Inc.
2 # All Rights Reserved.
3 #
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
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
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
14 #    under the License.
15
16 from sqlalchemy.orm import exc
17
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
25
26 # resource name using in callbacks
27 RBAC_POLICY = 'rbac-policy'
28
29
30 class RbacPluginMixin(common_db_mixin.CommonDbMixin):
31     """Plugin mixin that implements the RBAC DB operations."""
32
33     object_type_cache = {}
34     supported_extension_aliases = ['rbac-policies']
35
36     def create_rbac_policy(self, context, rbac_policy):
37         e = rbac_policy['rbac_policy']
38         try:
39             registry.notify(RBAC_POLICY, events.BEFORE_CREATE, self,
40                             context=context, object_type=e['object_type'],
41                             policy=e)
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'],
48                                action=e['action'],
49                                tenant_id=e['tenant_id'])
50             context.session.add(db_entry)
51         return self._make_rbac_policy_dict(db_entry)
52
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)
58
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']
63         try:
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'],
69                                            details=ex)
70         with context.session.begin(subtransactions=True):
71             entry.update(pol)
72         return self._make_rbac_policy_dict(entry)
73
74     def delete_rbac_policy(self, context, id):
75         entry = self._get_rbac_policy(context, id)
76         object_type = entry['object_type']
77         try:
78             registry.notify(RBAC_POLICY, events.BEFORE_DELETE, self,
79                             context=context, object_type=object_type,
80                             policy=entry)
81         except c_exc.CallbackFailure as ex:
82             raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'],
83                                            details=ex)
84         with context.session.begin(subtransactions=True):
85             context.session.delete(entry)
86         self.object_type_cache.pop(id, None)
87
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]
91         try:
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)
96
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)
100
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)
108
109     def _get_object_type(self, context, entry_id):
110         """Scans all RBAC tables for an ID to figure out the type.
111
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.
114         """
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
121                 return otype
122         raise ext_rbac.RbacPolicyNotFound(id=entry_id, object_type='unknown')