1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
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 Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
23 from openerp import SUPERUSER_ID
24 from openerp import tools
25 from openerp.osv import fields, osv, expression
26 from openerp.tools.safe_eval import safe_eval as eval
27 from openerp.tools.misc import unquote as unquote
29 class ir_rule(osv.osv):
32 _MODES = ['read', 'write', 'create', 'unlink']
34 def _eval_context_for_combinations(self):
35 """Returns a dictionary to use as evaluation context for
36 ir.rule domains, when the goal is to obtain python lists
37 that are easier to parse and combine, but not to
38 actually execute them."""
39 return {'user': unquote('user'),
40 'time': unquote('time')}
42 def _eval_context(self, cr, uid):
43 """Returns a dictionary to use as evaluation context for
45 return {'user': self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid),
48 def _domain_force_get(self, cr, uid, ids, field_name, arg, context=None):
50 eval_context = self._eval_context(cr, uid)
51 for rule in self.browse(cr, uid, ids, context):
53 res[rule.id] = expression.normalize_domain(eval(rule.domain_force, eval_context))
58 def _get_value(self, cr, uid, ids, field_name, arg, context=None):
60 for rule in self.browse(cr, uid, ids, context):
67 def _check_model_obj(self, cr, uid, ids, context=None):
68 return not any(self.pool[rule.model_id.model].is_transient() for rule in self.browse(cr, uid, ids, context))
70 def _check_model_name(self, cr, uid, ids, context=None):
71 # Don't allow rules on rules records (this model).
72 return not any(rule.model_id.model == self._name for rule in self.browse(cr, uid, ids, context))
75 'name': fields.char('Name', size=128, select=1),
76 'active': fields.boolean('Active', help="If you uncheck the active field, it will disable the record rule without deleting it (if you delete a native record rule, it may be re-created when you reload the module."),
77 'model_id': fields.many2one('ir.model', 'Object',select=1, required=True, ondelete="cascade"),
78 'global': fields.function(_get_value, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
79 'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
80 'domain_force': fields.text('Domain'),
81 'domain': fields.function(_domain_force_get, string='Domain', type='text'),
82 'perm_read': fields.boolean('Apply for Read'),
83 'perm_write': fields.boolean('Apply for Write'),
84 'perm_create': fields.boolean('Apply for Create'),
85 'perm_unlink': fields.boolean('Apply for Delete')
88 _order = 'model_id DESC'
99 ('no_access_rights', 'CHECK (perm_read!=False or perm_write!=False or perm_create!=False or perm_unlink!=False)', 'Rule must have at least one checked access right !'),
102 (_check_model_obj, 'Rules can not be applied on Transient models.', ['model_id']),
103 (_check_model_name, 'Rules can not be applied on the Record Rules model.', ['model_id']),
107 def _compute_domain(self, cr, uid, model_name, mode="read"):
108 if mode not in self._MODES:
109 raise ValueError('Invalid mode: %r' % (mode,))
111 if uid == SUPERUSER_ID:
113 cr.execute("""SELECT r.id
115 JOIN ir_model m ON (r.model_id = m.id)
118 AND r.perm_""" + mode + """
119 AND (r.id IN (SELECT rule_group_id FROM rule_group_rel g_rel
120 JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid)
121 WHERE u_rel.uid = %s) OR r.global)""", (model_name, uid))
122 rule_ids = [x[0] for x in cr.fetchall()]
124 # browse user as super-admin root to avoid access errors!
125 user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid)
126 global_domains = [] # list of domains
127 group_domains = {} # map: group -> list of domains
128 for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
129 # read 'domain' as UID to have the correct eval context for the rule.
130 rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
131 dom = expression.normalize_domain(rule_domain)
132 for group in rule.groups:
133 if group in user.groups_id:
134 group_domains.setdefault(group, []).append(dom)
136 global_domains.append(dom)
137 # combine global domains and group domains
139 group_domain = expression.OR(map(expression.OR, group_domains.values()))
142 domain = expression.AND(global_domains + [group_domain])
146 def clear_cache(self, cr, uid):
147 self._compute_domain.clear_cache(self)
149 def domain_get(self, cr, uid, model_name, mode='read', context=None):
150 dom = self._compute_domain(cr, uid, model_name, mode)
152 # _where_calc is called as superuser. This means that rules can
153 # involve objects on which the real uid has no acces rights.
154 # This means also there is no implicit restriction (e.g. an object
155 # references another object the user can't see).
156 query = self.pool[model_name]._where_calc(cr, SUPERUSER_ID, dom, active_test=False)
157 return query.where_clause, query.where_clause_params, query.tables
158 return [], [], ['"' + self.pool[model_name]._table + '"']
160 def unlink(self, cr, uid, ids, context=None):
161 res = super(ir_rule, self).unlink(cr, uid, ids, context=context)
162 self.clear_cache(cr, uid)
165 def create(self, cr, uid, vals, context=None):
166 res = super(ir_rule, self).create(cr, uid, vals, context=context)
167 self.clear_cache(cr, uid)
170 def write(self, cr, uid, ids, vals, context=None):
171 res = super(ir_rule, self).write(cr, uid, ids, vals, context=context)
172 self.clear_cache(cr,uid)
175 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: