You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
10 KiB

  1. ###################################################################################
  2. #
  3. # Copyright (C) 2017 MuK IT GmbH
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. ###################################################################################
  19. import logging
  20. from odoo import _, SUPERUSER_ID
  21. from odoo import models, api, fields
  22. from odoo.exceptions import AccessError
  23. from odoo.addons.muk_security.tools.security import NoSecurityUid
  24. _logger = logging.getLogger(__name__)
  25. class AccessGroupsModel(models.AbstractModel):
  26. _name = 'muk_security.mixins.access_groups'
  27. _description = "Group Access Mixin"
  28. _inherit = 'muk_security.mixins.access'
  29. # Set it to True to enforced security even if no group has been set
  30. _strict_security = False
  31. # If set the group fields are restricted by the access group
  32. _field_groups = None
  33. # If set to True the model is extended by fields to suspend the checks
  34. _active_suspend = False
  35. # If set the suspend fields are restricted by the access group
  36. _suspend_groups = None
  37. #----------------------------------------------------------
  38. # Datebase
  39. #----------------------------------------------------------
  40. @api.model
  41. def _add_magic_fields(self):
  42. super(AccessGroupsModel, self)._add_magic_fields()
  43. def add(name, field):
  44. if name not in self._fields:
  45. self._add_field(name, field)
  46. if self._active_suspend:
  47. add('suspend_security_read', fields.Boolean(
  48. _module=self._module,
  49. string="Suspend Security for Read",
  50. automatic=True,
  51. default=False,
  52. groups=self._suspend_groups))
  53. add('suspend_security_create', fields.Boolean(
  54. _module=self._module,
  55. string="Suspend Security for Create",
  56. automatic=True,
  57. default=False,
  58. groups=self._suspend_groups))
  59. add('suspend_security_write', fields.Boolean(
  60. _module=self._module,
  61. string="Suspend Security for Write",
  62. automatic=True,
  63. default=False,
  64. groups=self._suspend_groups))
  65. add('suspend_security_unlink', fields.Boolean(
  66. _module=self._module,
  67. string="Suspend Security for Unlink",
  68. automatic=True,
  69. default=False,
  70. groups=self._suspend_groups))
  71. add('groups', fields.Many2many(
  72. _module=self._module,
  73. comodel_name='muk_security.access_groups',
  74. relation='%s_groups_rel' % (self._table),
  75. column1='aid',
  76. column2='gid',
  77. string="Groups",
  78. automatic=True,
  79. groups=self._field_groups))
  80. add('complete_groups', fields.Many2many(
  81. _module=self._module,
  82. comodel_name='muk_security.access_groups',
  83. relation='%s_complete_groups_rel' % (self._table),
  84. column1='aid',
  85. column2='gid',
  86. string="Complete Groups",
  87. compute='_compute_groups',
  88. store=True,
  89. automatic=True,
  90. groups=self._field_groups))
  91. #----------------------------------------------------------
  92. # Function
  93. #----------------------------------------------------------
  94. @api.model
  95. def _get_no_access_ids(self):
  96. if not self._strict_security:
  97. sql = '''
  98. SELECT id
  99. FROM %s a
  100. WHERE NOT EXISTS (
  101. SELECT *
  102. FROM %s_complete_groups_rel r
  103. WHERE r.aid = a.id
  104. );
  105. ''' % (self._table, self._table)
  106. self.env.cr.execute(sql)
  107. fetch = self.env.cr.fetchall()
  108. return len(fetch) > 0 and list(map(lambda x: x[0], fetch)) or []
  109. else:
  110. return []
  111. @api.model
  112. def _get_suspended_access_ids(self, operation):
  113. if self._active_suspend:
  114. sql = '''
  115. SELECT id
  116. FROM %s a
  117. WHERE a.suspend_security_%s = true
  118. ''' % (self._table, operation)
  119. self.env.cr.execute(sql)
  120. fetch = self.env.cr.fetchall()
  121. return len(fetch) > 0 and list(map(lambda x: x[0], fetch)) or []
  122. else:
  123. return []
  124. @api.model
  125. def _get_access_ids(self):
  126. model = self._name.split(".")[-1]
  127. sql = '''
  128. SELECT r.aid
  129. FROM %s_complete_groups_rel r
  130. JOIN muk_security_access_groups g ON r.gid = g.id
  131. JOIN muk_security_access_groups_users_rel u ON r.gid = u.gid
  132. WHERE u.uid = %s AND g.perm_read = true
  133. ''' % (self._table, self.env.user.id)
  134. self.env.cr.execute(sql)
  135. fetch = self.env.cr.fetchall()
  136. access_ids = len(fetch) > 0 and list(map(lambda x: x[0], fetch)) or []
  137. return access_ids
  138. @api.model
  139. def _get_ids_without_security(self, operation):
  140. no_access_ids = self._get_no_access_ids()
  141. suspended_access_ids = self._get_suspended_access_ids(operation)
  142. return list(set(no_access_ids).union(suspended_access_ids))
  143. @api.model
  144. def _get_complete_access_ids(self, operation):
  145. access_ids = self._get_access_ids()
  146. no_access_ids = self._get_no_access_ids()
  147. suspended_access_ids = self._get_suspended_access_ids(operation)
  148. return list(set(access_ids).union(no_access_ids, suspended_access_ids))
  149. @api.multi
  150. def _eval_access_skip(self, operation):
  151. if isinstance(self.env.uid, NoSecurityUid):
  152. return True
  153. return False
  154. @api.multi
  155. def check_access_groups(self, operation):
  156. if self.env.user.id == SUPERUSER_ID or self._eval_access_skip(operation):
  157. return None
  158. filter_ids = self._get_ids_without_security(operation)
  159. for record in self.filtered(lambda rec: rec.id not in filter_ids):
  160. sql = '''
  161. SELECT perm_%s
  162. FROM %s_complete_groups_rel r
  163. JOIN muk_security_access_groups g ON r.gid = g.id
  164. JOIN muk_security_access_groups_users_rel u ON r.gid = u.gid
  165. WHERE r.aid = %s AND u.uid = %s
  166. ''' % (operation, self._table, record.id, self.env.user.id)
  167. self.env.cr.execute(sql)
  168. fetch = self.env.cr.fetchall()
  169. if not any(list(map(lambda x: x[0], fetch))):
  170. raise AccessError(_("This operation is forbidden!"))
  171. @api.multi
  172. def check_access(self, operation, raise_exception=False):
  173. res = super(BaseModelAccessGroups, self).check_access(operation, raise_exception)
  174. try:
  175. access_groups = self.check_access_groups(operation) == None
  176. return res and access_groups
  177. except AccessError:
  178. if raise_exception:
  179. raise
  180. return False
  181. #----------------------------------------------------------
  182. # Read
  183. #----------------------------------------------------------
  184. @api.multi
  185. def _after_read(self, result, *largs, **kwargs):
  186. result = super(BaseModelAccessGroups, self)._after_read(result)
  187. if self.env.user.id == SUPERUSER_ID or self._eval_access_skip("read"):
  188. return result
  189. access_ids = self._get_complete_access_ids("read")
  190. result = [result] if not isinstance(result, list) else result
  191. if len(access_ids) > 0:
  192. access_result = []
  193. for record in result:
  194. if record['id'] in access_ids:
  195. access_result.append(record)
  196. return access_result
  197. return []
  198. @api.model
  199. def _after_search(self, result, *largs, **kwargs):
  200. result = super(BaseModelAccessGroups, self)._after_search(result)
  201. if self.env.user.id == SUPERUSER_ID or self._eval_access_skip("read"):
  202. return result
  203. access_ids = self._get_complete_access_ids("read")
  204. if len(access_ids) > 0:
  205. access_result = self.env[self._name]
  206. if isinstance(result, int):
  207. if result in access_ids:
  208. return result
  209. else:
  210. for record in result:
  211. if record.id in access_ids:
  212. access_result += record
  213. return access_result
  214. return self.env[self._name]
  215. @api.model
  216. def _after_name_search(self, result, *largs, **kwargs):
  217. result = super(BaseModelAccessGroups, self)._after_name_search(result)
  218. if self.env.user.id == SUPERUSER_ID or self._eval_access_skip("read"):
  219. return result
  220. access_ids = self._get_complete_access_ids("read")
  221. if len(access_ids) > 0:
  222. access_result = []
  223. for tuple in result:
  224. if tuple[0] in access_ids:
  225. access_result.append(tuple)
  226. return access_result
  227. return []
  228. #----------------------------------------------------------
  229. # Read, View
  230. #----------------------------------------------------------
  231. @api.depends('groups')
  232. def _compute_groups(self):
  233. for record in self:
  234. record.complete_groups = record.groups
  235. #----------------------------------------------------------
  236. # Create, Update, Delete
  237. #----------------------------------------------------------
  238. @api.multi
  239. def _before_write(self, vals, *largs, **kwargs):
  240. self.check_access_groups('write')
  241. return super(BaseModelAccessGroups, self)._before_write(vals, *largs, **kwargs)
  242. @api.multi
  243. def _before_unlink(self, *largs, **kwargs):
  244. self.check_access_groups('unlink')
  245. return super(BaseModelAccessGroups, self)._before_unlink(*largs, **kwargs)