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 ##############################################################################
22 from osv import fields, osv, expression
24 from operator import itemgetter
25 from functools import partial
27 from tools.safe_eval import safe_eval as eval
28 from tools.misc import unquote as unquote
29 from openerp import SUPERUSER_ID
31 class ir_rule(osv.osv):
34 _MODES = ['read', 'write', 'create', 'unlink']
36 def _eval_context_for_combinations(self):
37 """Returns a dictionary to use as evaluation context for
38 ir.rule domains, when the goal is to obtain python lists
39 that are easier to parse and combine, but not to
40 actually execute them."""
41 return {'user': unquote('user'),
42 'time': unquote('time')}
44 def _eval_context(self, cr, uid):
45 """Returns a dictionary to use as evaluation context for
47 return {'user': self.pool.get('res.users').browse(cr, 1, uid),
50 def _domain_force_get(self, cr, uid, ids, field_name, arg, context=None):
52 eval_context = self._eval_context(cr, uid)
53 for rule in self.browse(cr, uid, ids, context):
55 res[rule.id] = expression.normalize(eval(rule.domain_force, eval_context))
60 def _get_value(self, cr, uid, ids, field_name, arg, context=None):
62 for rule in self.browse(cr, uid, ids, context):
69 def _check_model_obj(self, cr, uid, ids, context=None):
70 return not any(self.pool.get(rule.model_id.model).is_transient() for rule in self.browse(cr, uid, ids, context))
73 'name': fields.char('Name', size=128, select=1),
74 'model_id': fields.many2one('ir.model', 'Object',select=1, required=True),
75 '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"),
76 'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
77 'domain_force': fields.text('Domain'),
78 'domain': fields.function(_domain_force_get, string='Domain', type='text'),
79 'perm_read': fields.boolean('Apply For Read'),
80 'perm_write': fields.boolean('Apply For Write'),
81 'perm_create': fields.boolean('Apply For Create'),
82 'perm_unlink': fields.boolean('Apply For Delete')
85 _order = 'model_id DESC'
95 ('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 !'),
98 (_check_model_obj, 'Rules are not supported for osv_memory objects !', ['model_id'])
102 def _compute_domain(self, cr, uid, model_name, mode="read"):
103 if mode not in self._MODES:
104 raise ValueError('Invalid mode: %r' % (mode,))
106 if uid == SUPERUSER_ID:
108 cr.execute("""SELECT r.id
110 JOIN ir_model m ON (r.model_id = m.id)
112 AND r.perm_""" + mode + """
113 AND (r.id IN (SELECT rule_group_id FROM rule_group_rel g_rel
114 JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid)
115 WHERE u_rel.uid = %s) OR r.global)""", (model_name, uid))
116 rule_ids = [x[0] for x in cr.fetchall()]
118 # browse user as super-admin root to avoid access errors!
119 user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid)
120 global_domains = [] # list of domains
121 group_domains = {} # map: group -> list of domains
122 for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
123 # read 'domain' as UID to have the correct eval context for the rule.
124 rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
125 dom = expression.normalize(rule_domain)
126 for group in rule.groups:
127 if group in user.groups_id:
128 group_domains.setdefault(group, []).append(dom)
130 global_domains.append(dom)
131 # combine global domains and group domains
133 group_domain = expression.OR(map(expression.OR, group_domains.values()))
136 domain = expression.AND(global_domains + [group_domain])
140 def clear_cache(self, cr, uid):
141 self._compute_domain.clear_cache(self)
143 def domain_get(self, cr, uid, model_name, mode='read', context=None):
144 dom = self._compute_domain(cr, uid, model_name, mode)
146 # _where_calc is called as superuser. This means that rules can
147 # involve objects on which the real uid has no acces rights.
148 # This means also there is no implicit restriction (e.g. an object
149 # references another object the user can't see).
150 query = self.pool.get(model_name)._where_calc(cr, 1, dom, active_test=False)
151 return query.where_clause, query.where_clause_params, query.tables
152 return [], [], ['"'+self.pool.get(model_name)._table+'"']
154 def unlink(self, cr, uid, ids, context=None):
155 res = super(ir_rule, self).unlink(cr, uid, ids, context=context)
156 self.clear_cache(cr, uid)
159 def create(self, cr, uid, vals, context=None):
160 res = super(ir_rule, self).create(cr, uid, vals, context=context)
161 self.clear_cache(cr, uid)
164 def write(self, cr, uid, ids, vals, context=None):
165 res = super(ir_rule, self).write(cr, uid, ids, vals, context=context)
166 self.clear_cache(cr,uid)
171 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: