Add option to Eliminate user and fields in audit logs
parent
9018e8414f
commit
470d8def8a
|
@ -134,6 +134,19 @@ class AuditlogRule(models.Model):
|
|||
capture_record = fields.Boolean(
|
||||
help="Select this if you want to keep track of Unlink Record",
|
||||
)
|
||||
users_to_exclude_ids = fields.Many2many(
|
||||
"res.users",
|
||||
string="Users to Exclude",
|
||||
context={"active_test": False},
|
||||
states={"subscribed": [("readonly", True)]},
|
||||
)
|
||||
|
||||
fields_to_exclude_ids = fields.Many2many(
|
||||
"ir.model.fields",
|
||||
domain="[('model_id', '=', model_id)]",
|
||||
string="Fields to Exclude",
|
||||
states={"subscribed": [("readonly", True)]},
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
|
@ -256,6 +269,7 @@ class AuditlogRule(models.Model):
|
|||
"""Instanciate a create method that log its calls."""
|
||||
self.ensure_one()
|
||||
log_type = self.log_type
|
||||
users_to_exclude = self.mapped("users_to_exclude_ids")
|
||||
|
||||
@api.model_create_multi
|
||||
@api.returns("self", lambda value: value.id)
|
||||
|
@ -277,6 +291,8 @@ class AuditlogRule(models.Model):
|
|||
new_values[new_record.id][fname] = field.convert_to_read(
|
||||
new_record[fname], new_record
|
||||
)
|
||||
if self.env.user in users_to_exclude:
|
||||
return new_records
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -298,6 +314,8 @@ class AuditlogRule(models.Model):
|
|||
new_values = {}
|
||||
for vals, new_record in zip(vals_list2, new_records):
|
||||
new_values.setdefault(new_record.id, vals)
|
||||
if self.env.user in users_to_exclude:
|
||||
return new_records
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -315,6 +333,7 @@ class AuditlogRule(models.Model):
|
|||
"""Instanciate a read method that log its calls."""
|
||||
self.ensure_one()
|
||||
log_type = self.log_type
|
||||
users_to_exclude = self.mapped("users_to_exclude_ids")
|
||||
|
||||
def read(self, fields=None, load="_classic_read", **kwargs):
|
||||
result = read.origin(self, fields, load, **kwargs)
|
||||
|
@ -334,6 +353,8 @@ class AuditlogRule(models.Model):
|
|||
return result
|
||||
self = self.with_context(auditlog_disabled=True)
|
||||
rule_model = self.env["auditlog.rule"]
|
||||
if self.env.user in users_to_exclude:
|
||||
return result
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -351,6 +372,7 @@ class AuditlogRule(models.Model):
|
|||
"""Instanciate a write method that log its calls."""
|
||||
self.ensure_one()
|
||||
log_type = self.log_type
|
||||
users_to_exclude = self.mapped("users_to_exclude_ids")
|
||||
|
||||
def write_full(self, vals, **kwargs):
|
||||
self = self.with_context(auditlog_disabled=True)
|
||||
|
@ -369,6 +391,8 @@ class AuditlogRule(models.Model):
|
|||
.with_context(prefetch_fields=False)
|
||||
.read(fields_list)
|
||||
}
|
||||
if self.env.user in users_to_exclude:
|
||||
return result
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -391,6 +415,8 @@ class AuditlogRule(models.Model):
|
|||
old_values = {id_: old_vals2 for id_ in self.ids}
|
||||
new_values = {id_: vals2 for id_ in self.ids}
|
||||
result = write_fast.origin(self, vals, **kwargs)
|
||||
if self.env.user in users_to_exclude:
|
||||
return result
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -408,6 +434,7 @@ class AuditlogRule(models.Model):
|
|||
"""Instanciate an unlink method that log its calls."""
|
||||
self.ensure_one()
|
||||
log_type = self.log_type
|
||||
users_to_exclude = self.mapped("users_to_exclude_ids")
|
||||
|
||||
def unlink_full(self, **kwargs):
|
||||
self = self.with_context(auditlog_disabled=True)
|
||||
|
@ -419,6 +446,8 @@ class AuditlogRule(models.Model):
|
|||
.with_context(prefetch_fields=False)
|
||||
.read(fields_list)
|
||||
}
|
||||
if self.env.user in users_to_exclude:
|
||||
return unlink_full.origin(self, **kwargs)
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -433,6 +462,8 @@ class AuditlogRule(models.Model):
|
|||
def unlink_fast(self, **kwargs):
|
||||
self = self.with_context(auditlog_disabled=True)
|
||||
rule_model = self.env["auditlog.rule"]
|
||||
if self.env.user in users_to_exclude:
|
||||
return unlink_fast.origin(self, **kwargs)
|
||||
rule_model.sudo().create_logs(
|
||||
self.env.uid,
|
||||
self._name,
|
||||
|
@ -466,17 +497,16 @@ class AuditlogRule(models.Model):
|
|||
log_model = self.env["auditlog.log"]
|
||||
http_request_model = self.env["auditlog.http.request"]
|
||||
http_session_model = self.env["auditlog.http.session"]
|
||||
model_model = self.env[res_model]
|
||||
model_id = self.pool._auditlog_model_cache[res_model]
|
||||
auditlog_rule = self.env["auditlog.rule"].search([("model_id", "=", model_id)])
|
||||
fields_to_exclude = auditlog_rule.fields_to_exclude_ids.mapped("name")
|
||||
for res_id in res_ids:
|
||||
model_model = self.env[res_model]
|
||||
name = model_model.browse(res_id).name_get()
|
||||
model_id = self.pool._auditlog_model_cache[res_model]
|
||||
auditlog_rule = self.env["auditlog.rule"].search(
|
||||
[("model_id", "=", model_id)]
|
||||
)
|
||||
res_name = name and name[0] and name[0][1]
|
||||
vals = {
|
||||
"name": res_name,
|
||||
"model_id": self.pool._auditlog_model_cache[res_model],
|
||||
"model_id": model_id,
|
||||
"res_id": res_id,
|
||||
"method": method,
|
||||
"user_id": uid,
|
||||
|
@ -489,18 +519,26 @@ class AuditlogRule(models.Model):
|
|||
new_values.get(res_id, EMPTY_DICT), old_values.get(res_id, EMPTY_DICT)
|
||||
)
|
||||
if method == "create":
|
||||
self._create_log_line_on_create(log, diff.added(), new_values)
|
||||
self._create_log_line_on_create(
|
||||
log, diff.added(), new_values, fields_to_exclude
|
||||
)
|
||||
elif method == "read":
|
||||
self._create_log_line_on_read(
|
||||
log, list(old_values.get(res_id, EMPTY_DICT).keys()), old_values
|
||||
log,
|
||||
list(old_values.get(res_id, EMPTY_DICT).keys()),
|
||||
old_values,
|
||||
fields_to_exclude,
|
||||
)
|
||||
elif method == "write":
|
||||
self._create_log_line_on_write(
|
||||
log, diff.changed(), old_values, new_values
|
||||
log, diff.changed(), old_values, new_values, fields_to_exclude
|
||||
)
|
||||
elif method == "unlink" and auditlog_rule.capture_record:
|
||||
self._create_log_line_on_read(
|
||||
log, list(old_values.get(res_id, EMPTY_DICT).keys()), old_values
|
||||
log,
|
||||
list(old_values.get(res_id, EMPTY_DICT).keys()),
|
||||
old_values,
|
||||
fields_to_exclude,
|
||||
)
|
||||
|
||||
def _get_field(self, model, field_name):
|
||||
|
@ -525,11 +563,14 @@ class AuditlogRule(models.Model):
|
|||
cache[model.model][field_name] = field_data
|
||||
return cache[model.model][field_name]
|
||||
|
||||
def _create_log_line_on_read(self, log, fields_list, read_values):
|
||||
def _create_log_line_on_read(
|
||||
self, log, fields_list, read_values, fields_to_exclude
|
||||
):
|
||||
"""Log field filled on a 'read' operation."""
|
||||
log_line_model = self.env["auditlog.log.line"]
|
||||
fields_to_exclude = fields_to_exclude + FIELDS_BLACKLIST
|
||||
for field_name in fields_list:
|
||||
if field_name in FIELDS_BLACKLIST:
|
||||
if field_name in fields_to_exclude:
|
||||
continue
|
||||
field = self._get_field(log.model_id, field_name)
|
||||
# not all fields have an ir.models.field entry (ie. related fields)
|
||||
|
@ -556,11 +597,14 @@ class AuditlogRule(models.Model):
|
|||
vals["old_value_text"] = old_value_text
|
||||
return vals
|
||||
|
||||
def _create_log_line_on_write(self, log, fields_list, old_values, new_values):
|
||||
def _create_log_line_on_write(
|
||||
self, log, fields_list, old_values, new_values, fields_to_exclude
|
||||
):
|
||||
"""Log field updated on a 'write' operation."""
|
||||
log_line_model = self.env["auditlog.log.line"]
|
||||
fields_to_exclude = fields_to_exclude + FIELDS_BLACKLIST
|
||||
for field_name in fields_list:
|
||||
if field_name in FIELDS_BLACKLIST:
|
||||
if field_name in fields_to_exclude:
|
||||
continue
|
||||
field = self._get_field(log.model_id, field_name)
|
||||
# not all fields have an ir.models.field entry (ie. related fields)
|
||||
|
@ -605,11 +649,14 @@ class AuditlogRule(models.Model):
|
|||
vals["new_value_text"] = new_value_text
|
||||
return vals
|
||||
|
||||
def _create_log_line_on_create(self, log, fields_list, new_values):
|
||||
def _create_log_line_on_create(
|
||||
self, log, fields_list, new_values, fields_to_exclude
|
||||
):
|
||||
"""Log field filled on a 'create' operation."""
|
||||
log_line_model = self.env["auditlog.log.line"]
|
||||
fields_to_exclude = fields_to_exclude + FIELDS_BLACKLIST
|
||||
for field_name in fields_list:
|
||||
if field_name in FIELDS_BLACKLIST:
|
||||
if field_name in fields_to_exclude:
|
||||
continue
|
||||
field = self._get_field(log.model_id, field_name)
|
||||
# not all fields have an ir.models.field entry (ie. related fields)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# © 2021 Stefan Rijnhart <stefan@opener.amsterdam>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo.modules.migration import load_script
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests.common import Form, TransactionCase
|
||||
|
||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||
|
||||
|
@ -379,3 +379,200 @@ class TestAuditlogFullCaptureRecord(TransactionCase, AuditlogCommon):
|
|||
def tearDown(self):
|
||||
self.groups_rule.unlink()
|
||||
super(TestAuditlogFullCaptureRecord, self).tearDown()
|
||||
|
||||
|
||||
class AuditLogRuleTestForUserFields(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(AuditLogRuleTestForUserFields, cls).setUpClass()
|
||||
# get Contact model id
|
||||
cls.contact_model_id = (
|
||||
cls.env["ir.model"].search([("model", "=", "res.partner")]).id
|
||||
)
|
||||
|
||||
# get phone field id
|
||||
cls.fields_to_exclude_ids = (
|
||||
cls.env["ir.model.fields"]
|
||||
.search([("model", "=", "res.partner"), ("name", "=", "phone")])
|
||||
.id
|
||||
)
|
||||
|
||||
# get user id
|
||||
cls.user = (
|
||||
cls.env["res.users"]
|
||||
.with_context(no_reset_password=True, tracking_disable=True)
|
||||
.create(
|
||||
{
|
||||
"name": "Test User",
|
||||
"login": "testuser",
|
||||
}
|
||||
)
|
||||
)
|
||||
cls.user_2 = (
|
||||
cls.env["res.users"]
|
||||
.with_context(no_reset_password=True, tracking_disable=True)
|
||||
.create(
|
||||
{
|
||||
"name": "Test User2",
|
||||
"login": "testuser2",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
cls.users_to_exclude_ids = cls.user.id
|
||||
|
||||
# creating auditlog.rule
|
||||
cls.auditlog_rule = (
|
||||
cls.env["auditlog.rule"]
|
||||
.with_context(tracking_disable=True)
|
||||
.create(
|
||||
{
|
||||
"name": "testrule 01",
|
||||
"model_id": cls.contact_model_id,
|
||||
"log_read": True,
|
||||
"log_create": True,
|
||||
"log_write": True,
|
||||
"log_unlink": True,
|
||||
"log_type": "full",
|
||||
"capture_record": True,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# Updating phone in fields_to_exclude_ids
|
||||
cls.auditlog_rule.fields_to_exclude_ids = [[4, cls.fields_to_exclude_ids]]
|
||||
|
||||
# Updating users_to_exclude_ids
|
||||
cls.auditlog_rule.users_to_exclude_ids = [[4, cls.users_to_exclude_ids]]
|
||||
|
||||
# Subscribe auditlog.rule
|
||||
cls.auditlog_rule.subscribe()
|
||||
|
||||
cls.auditlog_log = cls.env["auditlog.log"]
|
||||
|
||||
# Creating new res.partner
|
||||
cls.testpartner1 = (
|
||||
cls.env["res.partner"]
|
||||
.with_context(tracking_disable=True)
|
||||
.create(
|
||||
{
|
||||
"name": "testpartner1",
|
||||
"phone": "123",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# Creating new res.partner from excluded user
|
||||
cls.testpartner2 = (
|
||||
cls.env["res.partner"]
|
||||
.with_context(tracking_disable=True)
|
||||
.with_user(cls.user.id)
|
||||
.create(
|
||||
{
|
||||
"name": "testpartner2",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def test_01_AuditlogFull_field_exclude_create_log(self):
|
||||
# Checking log is created for testpartner1
|
||||
create_log_record = self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "create"),
|
||||
("res_id", "=", self.testpartner1.id),
|
||||
]
|
||||
).ensure_one()
|
||||
self.assertTrue(create_log_record)
|
||||
field_names = create_log_record.line_ids.mapped("field_name")
|
||||
|
||||
# Checking log lines not created for phone
|
||||
self.assertTrue("phone" not in field_names)
|
||||
|
||||
# Removing created log record
|
||||
create_log_record.unlink()
|
||||
|
||||
def test_02_AuditlogFull_field_exclude_write_log(self):
|
||||
# Checking fields_to_exclude_ids
|
||||
self.testpartner1.with_context(tracking_disable=True).write(
|
||||
{
|
||||
"phone": "1234567890",
|
||||
}
|
||||
)
|
||||
# Checking log is created for testpartner1
|
||||
write_log_record = self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "write"),
|
||||
("res_id", "=", self.testpartner1.id),
|
||||
]
|
||||
).ensure_one()
|
||||
self.assertTrue(write_log_record)
|
||||
field_names = write_log_record.line_ids.mapped("field_name")
|
||||
|
||||
# Checking log lines not created for phone
|
||||
self.assertTrue("phone" not in field_names)
|
||||
|
||||
def test_03_AuditlogFull_user_exclude_write_log(self):
|
||||
# Update email in Form view with excluded user
|
||||
partner_form = Form(
|
||||
self.testpartner1.with_user(self.user.id).with_context(
|
||||
tracking_disable=True
|
||||
)
|
||||
)
|
||||
partner_form.email = "vendor@mail.com"
|
||||
testpartner1 = partner_form.save()
|
||||
|
||||
# Checking write log not created
|
||||
with self.assertRaises(ValueError):
|
||||
self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "write"),
|
||||
("res_id", "=", testpartner1.id),
|
||||
("user_id", "=", self.user.id),
|
||||
]
|
||||
).ensure_one()
|
||||
|
||||
def test_04_AuditlogFull_user_exclude_create_log(self):
|
||||
# Checking create log not created for testpartner2
|
||||
with self.assertRaises(ValueError):
|
||||
self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "create"),
|
||||
("res_id", "=", self.testpartner2.id),
|
||||
]
|
||||
).ensure_one()
|
||||
|
||||
def test_05_AuditlogFull_user_exclude_unlink_log(self):
|
||||
# Removing testpartner2 from excluded user
|
||||
self.testpartner2.with_user(self.user).unlink()
|
||||
|
||||
# Checking delete log not created for testpartner2
|
||||
with self.assertRaises(ValueError):
|
||||
self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "unlink"),
|
||||
("res_id", "=", self.testpartner2.id),
|
||||
]
|
||||
).ensure_one()
|
||||
|
||||
def test_06_AuditlogFull_unlink_log(self):
|
||||
# Removing testpartner1 with user_2
|
||||
self.testpartner1.with_user(self.user_2).unlink()
|
||||
delete_log_record = self.auditlog_log.search(
|
||||
[
|
||||
("model_id", "=", self.auditlog_rule.model_id.id),
|
||||
("method", "=", "unlink"),
|
||||
("res_id", "=", self.testpartner1.id),
|
||||
("user_id", "=", self.user_2.id),
|
||||
]
|
||||
).ensure_one()
|
||||
|
||||
# Checking log lines are created
|
||||
self.assertTrue(delete_log_record)
|
||||
|
||||
# Removing auditlog_rule
|
||||
self.auditlog_rule.unlink()
|
||||
|
|
|
@ -44,6 +44,14 @@
|
|||
name="capture_record"
|
||||
attrs="{'invisible':['|' ,('log_type','!=', 'full'), ('log_unlink','!=', True)]}"
|
||||
/>
|
||||
<field
|
||||
name="users_to_exclude_ids"
|
||||
widget="many2many_tags"
|
||||
/>
|
||||
<field
|
||||
name="fields_to_exclude_ids"
|
||||
widget="many2many_tags"
|
||||
/>
|
||||
</group>
|
||||
<group colspan="1">
|
||||
<field name="log_read" />
|
||||
|
|
Loading…
Reference in New Issue