audit_rule.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # -*- encoding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. # Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. import logging
  22. from openerp import api, fields, models, SUPERUSER_ID, tools, _
  23. from openerp.modules.registry import RegistryManager
  24. from audit_decorator import audit_decorator
  25. _logger = logging.getLogger(__package__)
  26. class AuditRule(models.Model):
  27. _name = 'audit.rule'
  28. _description = 'Audit Rule'
  29. name = fields.Char(size=32, required=True)
  30. active = fields.Boolean(default=True)
  31. log_create = fields.Boolean('Log Creation', default=True)
  32. log_write = fields.Boolean('Log Update', default=True)
  33. log_unlink = fields.Boolean('Log Deletion', default=True)
  34. state = fields.Selection([('draft', 'Draft'), ('done', 'Done')], 'Status', default='draft', readonly=True)
  35. model_id = fields.Many2one('ir.model', 'Object', required=True,
  36. help='Select object for which you want to generate log.',
  37. domain=[('model', '!=', 'audit.log')],
  38. readonly=True, states={'draft': [('readonly', False)]})
  39. action_id = fields.Many2one('ir.actions.act_window', 'Client Action', readonly=True)
  40. values_id = fields.Many2one('ir.values', "Add in the 'More' menu", readonly=True)
  41. _sql_constraints = [
  42. ('model_uniq', 'unique(model_id)', 'There is already a rule defined on this object.\n'
  43. 'You cannot define another: please edit the existing one.'),
  44. ]
  45. @api.one
  46. def _add_action(self):
  47. if not self.action_id:
  48. vals = {
  49. 'name': _('View audit logs'),
  50. 'res_model': 'audit.log',
  51. 'src_model': self.model_id.model,
  52. 'domain': "[('model_id','=', %s), ('res_id', '=', active_id)]" % self.model_id.id,
  53. }
  54. self.action_id = self.env['ir.actions.act_window'].create(vals)
  55. @api.one
  56. def _add_values(self):
  57. if not self.values_id:
  58. self.env['ir.model.data'].ir_set('action', 'client_action_relate', 'view_log_' + self.model_id.model,
  59. [self.model_id.model], 'ir.actions.act_window,%s' % self.action_id.id,
  60. replace=True, isobject=True, xml_id=False)
  61. values = self.env['ir.values'].search([('model', '=', self.model_id.model),
  62. ('value', '=', 'ir.actions.act_window,%s' % self.action_id.id)])
  63. if values:
  64. self.values_id = values[0]
  65. @api.one
  66. def _activate(self):
  67. if self._context and \
  68. self._context.get('activation_in_progress'):
  69. return
  70. self = self.with_context(activation_in_progress=True)
  71. self._add_action()
  72. self._add_values()
  73. @api.one
  74. def _deactivate(self):
  75. if self.values_id:
  76. self.values_id.unlink()
  77. if self.action_id:
  78. self.action_id.unlink()
  79. @api.multi
  80. def update_rule(self, force_deactivation=False):
  81. for rule in self:
  82. if rule.active and not force_deactivation:
  83. rule._activate()
  84. else:
  85. rule._deactivate()
  86. return True
  87. @tools.cache()
  88. def _check_audit_rule(self, cr):
  89. ids = self.search(cr, SUPERUSER_ID, [])
  90. return {rule.model_id.model:
  91. {method: rule.id
  92. for method in ('create', 'write', 'unlink')
  93. if getattr(rule, 'log_%s' % method)}
  94. for rule in self.browse(cr, SUPERUSER_ID, ids)}
  95. def _register_hook(self, cr, ids=None):
  96. updated = False
  97. if not ids:
  98. ids = self.search(cr, SUPERUSER_ID, [])
  99. for rule in self.browse(cr, SUPERUSER_ID, ids):
  100. model_obj = self.pool.get(rule.model_id.model)
  101. if not model_obj:
  102. continue
  103. if rule.active and not hasattr(model_obj, 'audit_rule'):
  104. for method in ('create', 'write', 'unlink'):
  105. model_obj._patch_method(method, audit_decorator())
  106. model_obj.audit_rule = True
  107. updated = True
  108. if not rule.active and hasattr(model_obj, 'audit_rule'):
  109. for method_name in ('create', 'write', 'unlink'):
  110. method = getattr(model_obj, method_name)
  111. while hasattr(method, 'origin'):
  112. if method.__name__ == 'audit_wrapper':
  113. model_obj._revert_method(method_name)
  114. break
  115. method = method.origin
  116. del model_obj.audit_rule
  117. updated = True
  118. if updated:
  119. self.clear_caches()
  120. return updated
  121. @api.model
  122. @api.returns('self', lambda value: value.id)
  123. def create(self, vals):
  124. vals['state'] = 'done'
  125. rule = super(AuditRule, self).create(vals)
  126. rule.update_rule()
  127. if self._register_hook(rule.id):
  128. RegistryManager.signal_registry_change(self.env.cr.dbname)
  129. return rule
  130. @api.multi
  131. def write(self, vals):
  132. res = super(AuditRule, self).write(vals)
  133. self.update_rule()
  134. if self._register_hook(self._ids):
  135. RegistryManager.signal_registry_change(self.env.cr.dbname)
  136. return res
  137. @api.multi
  138. def unlink(self):
  139. self.update_rule(force_deactivation=True)
  140. return super(AuditRule, self).unlink()
  141. _ignored_fields = ['message_ids', 'message_last_post']
  142. @classmethod
  143. def _format_data_to_log(cls, old_values, new_values):
  144. data = {}
  145. for age in ('old', 'new'):
  146. vals_list = old_values if age == 'old' else new_values
  147. if isinstance(vals_list, dict):
  148. vals_list = [vals_list]
  149. for vals in vals_list or []:
  150. for field in cls._ignored_fields:
  151. vals.pop(field, None)
  152. res_id = vals.pop('id')
  153. if vals:
  154. data.setdefault(res_id, {'old': {}, 'new': {}})[age] = vals
  155. return data
  156. @api.one
  157. def log(self, method, old_values=None, new_values=None):
  158. _logger.debug('Starting audit log')
  159. data = self._format_data_to_log(old_values, new_values)
  160. for res_id in data:
  161. self.env['audit.log'].sudo().create({
  162. 'user_id': self._uid,
  163. 'model_id': self.sudo().model_id.id,
  164. 'method': method,
  165. 'res_id': res_id,
  166. 'data': repr(data[res_id]),
  167. })
  168. return True