Browse Source

[ADD] Custom methods logging

pull/1049/head
Eric Lembregts 7 years ago
committed by Holger Brunn
parent
commit
915feccae4
  1. 3
      auditlog/README.rst
  2. 1
      auditlog/models/__init__.py
  3. 15
      auditlog/models/methods.py
  4. 108
      auditlog/models/rule.py
  5. 2
      auditlog/security/ir.model.access.csv
  6. 56
      auditlog/tests/test_auditlog.py
  7. 16
      auditlog/views/auditlog_view.xml

3
auditlog/README.rst

@ -7,7 +7,8 @@ Audit Log - Track user operations
=================================
This module allows the administrator to log user operations performed on data
models such as ``create``, ``read``, ``write`` and ``delete``.
models such as ``create``, ``read``, ``write`` and ``delete``. Furthermore
it allows logging of custom methods.
Usage
=====

1
auditlog/models/__init__.py

@ -7,3 +7,4 @@ from . import http_session
from . import http_request
from . import log
from . import autovacuum
from . import methods

15
auditlog/models/methods.py

@ -0,0 +1,15 @@
# coding: utf-8
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import api, fields, models
class AuditlogMethods(models.Model):
_name = 'auditlog.methods'
_description = 'Auditlog custom methods'
_order = 'name'
name = fields.Char(required=True)
message = fields.Char(required=True)
rule_id = fields.Many2one('auditlog.rule', readonly=True)
use_active_ids = fields.Boolean(default=False)
context_field_number = fields.Integer(default=0)

108
auditlog/models/rule.py

@ -3,6 +3,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields, api, modules, _, SUPERUSER_ID, sql_db
from openerp.exceptions import ValidationError
FIELDS_BLACKLIST = [
'id', 'create_uid', 'create_date', 'write_uid', 'write_date',
@ -71,6 +72,11 @@ class AuditlogRule(models.Model):
u"Log Creates", default=True,
help=(u"Select this if you want to keep track of creation on any "
u"record of the model of this rule"))
log_custom = fields.Boolean(
u"Log Methods",
help=(u"Select this if you want to keep track of custom methods on "
u"any record of the model of this rule"))
custom_method_ids = fields.One2many('auditlog.methods', 'rule_id')
log_type = fields.Selection(
[('full', u"Full log"),
('fast', u"Fast log"),
@ -154,6 +160,29 @@ class AuditlogRule(models.Model):
model_model._patch_method('unlink', rule._make_unlink())
setattr(model_model, check_attr, True)
updated = True
# Check if custom methods are enabled and patch the different
# rule methods
if getattr(rule, 'log_custom'):
for custom_method in rule.custom_method_ids:
check_attr = 'auditlog_ruled_%s' % custom_method.name
if not hasattr(model_model, custom_method.name):
raise ValidationError(
_('Method %s does not exist for model %s.' % (
custom_method.name,
model_model
)))
if not hasattr(model_model, check_attr):
model_model._patch_method(
custom_method.name,
rule._make_custom(
custom_method.message,
custom_method.use_active_ids,
custom_method.context_field_number)
)
setattr(model_model, check_attr, True)
updated = True
return updated
@api.multi
@ -167,6 +196,12 @@ class AuditlogRule(models.Model):
getattr(model_model, method), 'origin'):
model_model._revert_method(method)
updated = True
if hasattr(rule, 'log_custom'):
for custom_method in rule.custom_method_ids:
method = custom_method.name
if hasattr(getattr(model_model, method), 'origin'):
model_model._revert_method(method)
updated = True
if updated:
modules.registry.RegistryManager.signal_registry_change(
self.env.cr.dbname)
@ -349,6 +384,79 @@ class AuditlogRule(models.Model):
return unlink_full if self.log_type == 'full' else unlink_fast
@api.multi
def _make_custom(self, message, use_active_ids, context_field_number):
"""Instanciate a read method that log its calls."""
self.ensure_one()
log_type = self.log_type
def custom(self, *args, **kwargs):
result = custom.origin(self, *args, **kwargs)
result2 = result
if not isinstance(result2, list):
result2 = [result]
# Old API
if args and isinstance(args[0], sql_db.Cursor):
cr, uid, ids = args[0], args[1], args[2]
if isinstance(ids, (int, long)):
ids = [ids]
context = kwargs.get('context', {})
# Set specific context if it is defined by our rule
if not context and context_field_number:
if context_field_number - 1 < len(args):
context = args[context_field_number - 1]
if context.get('auditlog_disabled'):
return result
env = api.Environment(cr, uid, {'auditlog_disabled': True})
rule_model = env['auditlog.rule']
# Overwrite the ids and object_model if it is required
# by the auditlog rule
object_model = self._name
if use_active_ids:
if context.get('active_model'):
if context.get('active_ids'):
object_model = context.get(
'active_model',
object_model)
ids = context.get('active_ids', ids)
rule_model.sudo().create_logs(
env.uid, object_model, ids,
message, None, None, {'log_type': log_type})
# New API
else:
if self.env.context.get('auditlog_disabled'):
return result
self = self.with_context(auditlog_disabled=True)
context = self.env.context
# Overwrite the ids and object_model if it is required
# by the auditlog rule
ids = self.ids
object_model = self._name
if use_active_ids:
if context.get('active_model'):
if context.get('active_ids'):
object_model = context.get(
'active_model',
object_model)
ids = context.get('active_ids', ids)
rule_model = self.env['auditlog.rule']
rule_model.sudo().create_logs(
self.env.uid, object_model, ids,
message, None, None, {'log_type': log_type})
return result
return custom
def create_logs(self, uid, res_model, res_ids, method,
old_values=None, new_values=None,
additional_log_values=None):

2
auditlog/security/ir.model.access.csv

@ -4,9 +4,11 @@ access_auditlog_log_user,auditlog_log_user,model_auditlog_log,base.group_user,0,
access_auditlog_log_line_user,auditlog_log_line_user,model_auditlog_log_line,base.group_user,0,0,0,0
access_auditlog_http_session_user,auditlog_http_session_user,model_auditlog_http_session,base.group_user,0,0,0,0
access_auditlog_http_request_user,auditlog_http_request_user,model_auditlog_http_request,base.group_user,0,0,0,0
access_auditlog_methods_user,auditlog_methods_user,model_auditlog_methods,base.group_user,0,0,0,0
access_auditlog_rule_manager,auditlog_rule_manager,model_auditlog_rule,base.group_erp_manager,1,1,1,1
access_auditlog_log_manager,auditlog_log_manager,model_auditlog_log,base.group_erp_manager,1,1,1,1
access_auditlog_log_line_manager,auditlog_log_line_manager,model_auditlog_log_line,base.group_erp_manager,1,1,1,1
access_auditlog_http_session_manager,auditlog_http_session_manager,model_auditlog_http_session,base.group_erp_manager,1,1,1,1
access_auditlog_http_request_manager,auditlog_http_request_manager,model_auditlog_http_request,base.group_erp_manager,1,1,1,1
access_auditlog_methods_manager,auditlog_methods_manager,model_auditlog_methods,base.group_erp_manager,1,1,1,1

56
auditlog/tests/test_auditlog.py

@ -114,3 +114,59 @@ class TestAuditlogFast(TransactionCase, TestAuditlog):
def tearDown(self):
self.groups_rule.unlink()
super(TestAuditlogFast, self).tearDown()
class TestMethods(TransactionCase):
def setUp(self):
super(TestMethods, self).setUp()
# Clear all existing logging lines
existing_audit_logs = self.env['auditlog.log'].search([])
existing_audit_logs.unlink()
# Create account period to test
self.partner = self.env['res.partner'].create({
'name': 'Test User'
})
self.partner_model = self.env['ir.model'].search([
('model', '=', 'res.partner')])
# Setup rule for model "account.period.close" and method "data_save"
self.auditlog_rule = self.env['auditlog.rule'].create({
'name': 'res.partner',
'model_id': self.partner_model.id,
'log_type': 'fast',
'log_read': False,
'log_create': False,
'log_write': False,
'log_unlink': False,
'log_custom': True,
'custom_method_ids': [(0, 0, {
'name': 'copy',
'message': 'execute_copy',
})]
})
self.auditlog_rule.subscribe()
def tearDown(self):
self.auditlog_rule.unsubscribe()
super(TestMethods, self).tearDown()
def test_01_subscribe_unsubscribe(self):
"""The test is subscribed by default, so let's try both"""
self.auditlog_rule.unsubscribe()
self.auditlog_rule.subscribe()
def test_02_copy_res_partner_logging(self):
self.partner.copy()
logs = self.env['auditlog.log'].search([
('res_id', '=', self.partner.id),
('model_id', '=', self.partner_model.id),
('method', '=', 'execute_copy')
])
self.assertEqual(len(logs), 1)

16
auditlog/views/auditlog_view.xml

@ -38,6 +38,21 @@
<!--<field name="log_workflow"/>-->
</group>
</group>
<group string="Custom Methods" attrs="{'invisible': [('log_custom', '=', False)]}">
<group colspan="2">
<div attrs="{'invisible': [('log_custom', '=', False)]}" colspan="2">
You can only edit custom methods when the rule is unsubscribed.
</div>
<field name="custom_method_ids" nolabel="1" attrs="{'readonly': [('state', '=', 'subscribed')]}" colspan="2">
<tree string="Custom Methods" editable="top">
<field name="name"/>
<field name="message"/>
<field name="use_active_ids"/>
<field name="context_field_number"/>
</tree>
</field>
</group>
</group>
<!--
<group string="Users">
<field name="user_ids" nolabel="1"/>
@ -60,6 +75,7 @@
<field name="log_write"/>
<field name="log_unlink"/>
<field name="log_create"/>
<field name="log_custom"/>
<!--<field name="log_action"/>-->
<!--<field name="log_workflow"/>-->
<field name="state"/>

Loading…
Cancel
Save