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.

218 lines
8.5 KiB

7 years ago
  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 os
  20. import hashlib
  21. import logging
  22. from odoo import _
  23. from odoo import models, api, fields
  24. from odoo.exceptions import AccessError
  25. _logger = logging.getLogger(__name__)
  26. class BaseModelLocking(models.AbstractModel):
  27. _name = 'muk_security.locking'
  28. _description = 'MuK Locking Model'
  29. _inherit = 'muk_utils.model'
  30. #----------------------------------------------------------
  31. # Database
  32. #----------------------------------------------------------
  33. locked = fields.Many2one(
  34. comodel_name='muk_security.lock',
  35. compute='_compute_lock',
  36. string="Locked",)
  37. editor = fields.Boolean(
  38. compute='_compute_editor',
  39. string="Editor")
  40. #----------------------------------------------------------
  41. # Locking
  42. #----------------------------------------------------------
  43. @api.multi
  44. def lock(self, user=None, operation=None, *largs, **kwargs):
  45. result = []
  46. for record in self:
  47. lock = record.is_locked()
  48. if lock and lock.operation and lock.operation == operation:
  49. result.append({
  50. 'record': record,
  51. 'lock': lock,
  52. 'token': lock.token})
  53. elif lock and ((lock.operation and lock.operation != operation) or not lock.operation):
  54. raise AccessError(_("The record (%s[%s]) is locked, so it can't be locked again.") %
  55. (record._name, record.id))
  56. else:
  57. token = hashlib.sha1(os.urandom(128)).hexdigest()
  58. lock = self.env['muk_security.lock'].sudo().create({
  59. 'locked_by': user and user.name or "System",
  60. 'locked_by_ref': user and user._name + ',' + str(user.id) or None,
  61. 'lock_ref': record._name + ',' + str(record.id),
  62. 'token': token,
  63. 'operation': operation})
  64. result.append({
  65. 'record': record,
  66. 'lock': lock,
  67. 'token': token})
  68. return result
  69. @api.multi
  70. def unlock(self, *largs, **kwargs):
  71. for record in self:
  72. locks = self.env['muk_security.lock'].sudo().search(
  73. [('lock_ref', '=', "%s,%s" % (record._name, str(record.id)))])
  74. locks.sudo().unlink()
  75. return True
  76. @api.model
  77. def unlock_operation(self, operation, *largs, **kwargs):
  78. locks = self.env['muk_security.lock'].sudo().search([('operation', '=', operation)])
  79. locks.sudo().unlink()
  80. return True
  81. @api.multi
  82. def is_locked(self, *largs, **kwargs):
  83. self.ensure_one()
  84. lock = self.env['muk_security.lock'].sudo().search(
  85. [('lock_ref', '=', self._name + ',' + str(self.id))], limit=1)
  86. if lock.id:
  87. return lock
  88. return False
  89. @api.multi
  90. def is_locked_by(self, *largs, **kwargs):
  91. self.ensure_one()
  92. lock = self.env['muk_security.lock'].sudo().search(
  93. [('lock_ref', '=', self._name + ',' + str(self.id))], limit=1)
  94. if lock.id:
  95. return lock.locked_by_ref
  96. return False
  97. @api.multi
  98. def _checking_lock_user(self, *largs, **kwargs):
  99. for record in self:
  100. lock = record.is_locked()
  101. if lock and lock.locked_by_ref and not lock.locked_by_ref != self.env.user:
  102. raise AccessError(_("The record (%s[%s]) is locked by a user, so it can't be changes or deleted.") %
  103. (self._name, self.id))
  104. @api.multi
  105. def _checking_lock(self, operation=None, *largs, **kwargs):
  106. self._checking_lock_user()
  107. for record in self:
  108. lock = record.is_locked()
  109. if lock and lock.operation and lock.operation != operation:
  110. raise AccessError(_("The record (%s[%s]) is locked, so it can't be changes or deleted.") %
  111. (self._name, self.id))
  112. @api.multi
  113. def user_lock(self, *largs, **kwargs):
  114. self.ensure_one()
  115. lock = self.is_locked()
  116. if lock:
  117. if lock.locked_by_ref:
  118. raise AccessError(_("The record is already locked by another user. (%s)") % lock.locked_by_ref.name)
  119. else:
  120. raise AccessError(_("The record is already locked."))
  121. return self.lock(user=self.env.user)
  122. @api.multi
  123. def user_unlock(self, *largs, **kwargs):
  124. self.ensure_one()
  125. lock = self.is_locked()
  126. if lock:
  127. if lock.locked_by_ref and lock.locked_by_ref.id == self.env.user.id:
  128. self.unlock()
  129. else:
  130. if lock.locked_by_ref:
  131. raise AccessError(_("The record is already locked by another user. (%s)") % lock.locked_by_ref.name)
  132. else:
  133. raise AccessError(_("The record is already locked."))
  134. #----------------------------------------------------------
  135. # Read, View
  136. #----------------------------------------------------------
  137. def _compute_lock(self):
  138. for record in self:
  139. record.locked = record.is_locked()
  140. @api.depends('locked')
  141. def _compute_editor(self):
  142. for record in self:
  143. record.editor = record.is_locked_by() == record.env.user
  144. #----------------------------------------------------------
  145. # Create, Update, Delete
  146. #----------------------------------------------------------
  147. @api.multi
  148. def write(self, vals):
  149. operation = hashlib.sha1(os.urandom(128)).hexdigest()
  150. vals = self._before_write_operation(vals, operation)
  151. result = super(BaseModelLocking, self).write(vals)
  152. for record in self:
  153. record._after_write_record_operation(vals, operation)
  154. result = self._after_write_operation(result, vals, operation)
  155. return result
  156. def _before_write_operation(self, vals, operation, *largs, **kwargs):
  157. if 'operation' in self.env.context:
  158. self._checking_lock(self.env.context['operation'])
  159. elif operation:
  160. self._checking_lock(operation)
  161. else:
  162. self._checking_lock_user()
  163. return vals
  164. def _after_write_record_operation(self, vals, operation, *largs, **kwargs):
  165. return vals
  166. def _after_write_operation(self, result, vals, operation, *largs, **kwargs):
  167. return result
  168. @api.multi
  169. def unlink(self):
  170. operation = hashlib.sha1(os.urandom(128)).hexdigest()
  171. operation_info = self._before_unlink_operation(operation)
  172. operation_infos = []
  173. for record in self:
  174. operation_infos.append(record._before_unlink_record_operation(operation))
  175. result = super(BaseModelLocking, self).unlink()
  176. self._after_unlink_operation(result, operation_info, operation_infos, operation)
  177. return result
  178. def _before_unlink_operation(self, operation, *largs, **kwargs):
  179. if 'operation' in self.env.context:
  180. self._checking_lock(self.env.context['operation'])
  181. elif operation:
  182. self._checking_lock(operation)
  183. else:
  184. self._checking_lock_user()
  185. return {}
  186. def _before_unlink_record_operation(self, operation, *largs, **kwargs):
  187. return {}
  188. def _after_unlink_operation(self, result, operation_info, operation_infos, operation, *largs, **kwargs):
  189. pass