Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 82e6001

Browse files
committed
[ADD] review mods 1
1 parent dced1d4 commit 82e6001

10 files changed

Lines changed: 621 additions & 106 deletions

File tree

auditlog_security/__manifest__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
33

44
{
5-
"name": "Audit Log Permissions",
5+
"name": "Audit Log User Permissions",
66
"version": "11.0.1.0.0",
77
"author": "Therp B.V.,Odoo Community Association (OCA)",
88
"license": "AGPL-3",
99
"website": "https://github.com/OCA/server-tools/",
1010
"category": "Tools",
11+
"description": """Allow regular users to view Audit log lines
12+
via the form view of the relevant model""",
1113
"depends": [
1214
"auditlog",
1315
"contacts",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
22

33
from . import auditlog_rule
4+
from . import auditlog_line_access_rule
45
from . import ir_rule
56
from . import auditlog_autovacuum

auditlog_security/models/auditlog_autovacuum.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class AuditlogAutovacuum(models.TransientModel):
88
_inherit = "auditlog.autovacuum"
99

1010
@api.model
11-
def autovaccum(self, days):
11+
def autovacuum(self, days):
1212
return super(
1313
AuditlogAutovacuum, self.with_context(auditlog_write=True)
1414
).autovacuum(days=days)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright 2021 Therp B.V.
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import exceptions, models, fields, api, modules, _
5+
from odoo.addons.auditlog.models.rule import FIELDS_BLACKLIST
6+
7+
8+
class AuditlogLineAccessRule(models.Model):
9+
_name = "auditlog.line.access.rule"
10+
11+
name = fields.char()
12+
13+
field_ids = fields.Many2many(
14+
"ir.model.fields"
15+
)
16+
group_ids = fields.Many2many(
17+
"res.groups",
18+
help="""Groups that will be allowed to see the logged fields, if left empty
19+
default will be all users with a login""",
20+
)
21+
model_id = fields.Many2one(
22+
'ir.model', related='auditlog_rule_id.model_id', readonly=True)
23+
auditlog_rule_id = fields.Many2one(
24+
'auditlog.rule', 'auditlog_access_rule_ids',
25+
readonly=True, ondelete='cascade'
26+
)
27+
28+
def needs_rule(self):
29+
self.ensure_one()
30+
return bool(self.group_ids)
31+
32+
def get_linked_rules(self):
33+
# return with context key so that deletion will not be forbidden
34+
return self.env["ir.rule"].search(
35+
[("auditlog_line_access_rule_id", "in", self.ids)])
36+
37+
def get_field_ids_domain(self):
38+
"""note this solution will work only with a hardcoded design of models,
39+
because on initialization , self.model_id.id still is not defined.
40+
for now, to keep generality we put the filtering in the view."""
41+
return [
42+
("model_id", "=", self.env.ref("base.model_res_partner").id),
43+
("name", "not in", FIELDS_BLACKLIST),
44+
]
45+
46+
def unlink(self):
47+
to_delete = self.get_linked_rules()
48+
res = super(AuditlogLineAccessRule, self).unlink()
49+
if res:
50+
res = res and to_delete.with_context(auditlog_write=True).unlink()
51+
return res
52+
53+
def add_default_group_if_needed(self):
54+
self.ensure_one()
55+
res = False
56+
if not self.group_ids and self.field_ids:
57+
res = self.with_context(no_iter=True).write(
58+
{"group_ids": [(6, 0, [self.env.ref("base.group_user").id])]})
59+
return res
60+
61+
@api.model
62+
def create(self, vals):
63+
res = super(AuditlogLineAccessRule, self).create(vals)
64+
res.add_default_group_if_needed()
65+
if res.needs_rule():
66+
res.generate_rules()
67+
return res
68+
69+
@api.multi
70+
def write(self, vals):
71+
res = super(AuditlogLineAccessRule, self).write(vals)
72+
for this in self:
73+
added = this.add_default_group_if_needed()
74+
if any([x in vals for x in ("group_ids", "field_ids", "model_id", "all_fields")]) or added:
75+
if this.needs_rule():
76+
this.generate_rules()
77+
else:
78+
this.get_linked_rules().with_context(auditlog_write=True).unlink()
79+
return res
80+
81+
def generate_rules(self):
82+
old_rule = self.env["ir.rule"].search([("auditlog_line_access_rule_id", "=", self.id)], limit=1)
83+
values = self._prepare_rule_values()
84+
if old_rule:
85+
old_rule.with_context(auditlog_write=True).write(values)
86+
else:
87+
self.with_context(auditlog_write=True).env["ir.rule"].create(values)
88+
89+
def _prepare_rule_values(self):
90+
domain_force = "[" + " ('aulditlog_rule_id.log_id.model_id' , '=', %s)," % (self.model_id.id)
91+
if self.field_ids:
92+
domain_force += "('field_id', 'in', %s)" % (self.field_ids.ids)
93+
domain_force += "]"
94+
return {
95+
"name": "auditlog_extended_%s" % self.id,
96+
"model_id": self.env.ref("auditlog.model_auditlog_log_line").id,
97+
"groups": [(6, 0, self.group_ids.ids)],
98+
"perm_read": True,
99+
"domain_force": domain_force,
100+
"auditlog_line_access_rule_id": self.id,
101+
}
102+

auditlog_security/models/auditlog_rule.py

Lines changed: 9 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,100 +8,27 @@
88
class AuditlogRule(models.Model):
99
_inherit = "auditlog.rule"
1010

11-
field_ids = fields.Many2many(
12-
"ir.model.fields"
13-
)
14-
group_ids = fields.Many2many(
15-
"res.groups",
16-
help="""Groups that will be allowed to see the logged fields, if left empty
17-
default will be all users with a login""",
11+
auditlog_line_access_rule_ids = fields.One2many(
12+
'auditlog.line.access.rule' , 'auditlog_rule_id',
13+
ondelete='cascade'
1814
)
1915

2016
@api.onchange("model_id")
2117
def onchange_model_id(self):
2218
# if model changes we must wipe out all field ids
23-
self.field_ids = False
24-
25-
def needs_rule(self):
26-
self.ensure_one()
27-
return bool(self.group_ids)
28-
29-
def get_linked_rules(self):
30-
# return with context key so that deletion will not be forbidden
31-
return self.with_context(auditlog_write=True).env["ir.rule"].search(
32-
[("auditlog_id", "in", self.ids)])
33-
34-
def get_field_ids_domain(self):
35-
"""note this solution will work only with a hardcoded design of models,
36-
because on initialization , self.model_id.id still is not defined.
37-
for now, to keep generality we put the filtering in the view."""
38-
return [
39-
("model_id", "=", self.env.ref("base.model_res_partner").id),
40-
("name", "not in", FIELDS_BLACKLIST),
41-
]
19+
self.auditlog_line_access_rule_ids.unlink()
4220

21+
@api.multi
4322
def unlink(self):
44-
# if we delete auditlog rule, corresponding ir.rules are removed
45-
# TODO PROPOSAL: a warning here with detailed information?
46-
to_delete = self.get_linked_rules()
23+
lines = self.mapped('auditlog_line_access_rule_ids')
4724
res = super(AuditlogRule, self).unlink()
4825
if res:
49-
res = res and to_delete.unlink()
50-
return res
51-
52-
def add_default_group_if_needed(self):
53-
self.ensure_one()
54-
res = False
55-
if not self.group_ids and self.field_ids:
56-
# if group has been removed and no group specified ad base user default
57-
res = self.with_context(no_iter=True).write(
58-
{"group_ids": [(6, 0, [self.env.ref("base.group_user").id])]})
59-
return res
60-
61-
@api.model
62-
def create(self, vals):
63-
res = super(AuditlogRule, self).create(vals)
64-
res.add_default_group_if_needed()
65-
if res.needs_rule():
66-
res.generate_rules()
26+
lines.unlink()
6727
return res
6828

69-
@api.multi
70-
def write(self, vals):
71-
res = super(AuditlogRule, self).write(vals)
72-
for this in self:
73-
added = this.add_default_group_if_needed()
74-
if any([x in vals for x in ("group_ids", "field_ids", "model_id", "all_fields")]) or added:
75-
if this.needs_rule():
76-
this.generate_rules()
77-
else:
78-
this.get_linked_rules().unlink()
79-
return res
80-
81-
def generate_rules(self):
82-
old_rule = self.env["ir.rule"].search([("auditlog_id", "=", self.id)], limit=1)
83-
values = self._prepare_rule_values()
84-
if old_rule:
85-
old_rule.with_context(auditlog_write=True).write(values)
86-
else:
87-
self.with_context(auditlog_write=True).env["ir.rule"].create(values)
88-
89-
def _prepare_rule_values(self):
90-
domain_force = "[" + " ('log_id.model_id' , '=', %s)," % (self.model_id.id)
91-
if self.field_ids:
92-
domain_force += "('field_id', 'in', %s)" % (self.field_ids.ids)
93-
domain_force += "]"
94-
return {
95-
"name": "auditlog_extended_%s" % self.id,
96-
"model_id": self.env.ref("auditlog.model_auditlog_log_line").id,
97-
"groups": [(6, 0, self.group_ids.ids)],
98-
"perm_read": True,
99-
"domain_force": domain_force,
100-
"auditlog_id": self.id,
101-
}
102-
10329
@api.multi
10430
def subscribe(self):
31+
super(AuditlogRule, self).subscribe()
10532
act_window_model = self.env['ir.actions.act_window']
10633
for rule in self:
10734
domain = "[('log_id.model_id', '=', %s), ('log_id.res_id', '=', active_id)]" % (
@@ -116,3 +43,4 @@ def subscribe(self):
11643
act_window = act_window_model.sudo().create(vals)
11744
rule.write({'state': 'subscribed', 'action_id': act_window.id})
11845
return True
46+

auditlog_security/models/ir_rule.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
class IrRule(models.Model):
88
_inherit = "ir.rule"
99

10-
auditlog_id = fields.Many2one(
11-
"auditlog.rule",
10+
auditlog_line_access_rule_id = fields.Many2one(
11+
"auditlog.access.line.rule",
1212
required=False,
13-
help="Auditlog Rule that generated this ir.rule",
13+
index=True,
14+
help="Auditlog line access Rule that generated this ir.rule",
1415
)
1516

1617
@api.model
@@ -30,14 +31,14 @@ def create(self, values):
3031
@api.multi
3132
def write(self, vals):
3233
if "auditlog_id" in vals and not self.env.context.get("auditlog_write"):
33-
raise exceptions.ValidationError(_("""Cannot change auditlog_id"""))
34+
raise exceptions.ValidationError(_("""Cannot change auditlog_access_line_rule"""))
3435
return super(IrRule, self).write(vals)
3536

3637
@api.multi
3738
def unlink(self):
3839
auditlog_write = self.env.context.get("auditlog_write")
3940
for this in self:
40-
if this.auditlog_id and not auditlog_write:
41+
if this.auditlog_line_access_rule_id and not auditlog_write:
4142
raise exceptions.ValidationError(
4243
_(
4344
"""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
22
access_auditlog_log_line_user,auditlog_log_line_user,model_auditlog_log_line,base.group_user,1,0,0,0
33
access_auditlog_log_user,auditlog_log_user,model_auditlog_log,base.group_user,1,0,0,0
4+
access_auditlog_line_access_rule_admin,auditlog_line_access_rule_admin,model_auditlog_line_access_rule,base.group_system,1,1,1,1

0 commit comments

Comments
 (0)