[IMP] access rights: improve error messages for ACLs and record rules
bzr revid: odo@openerp.com-20120615153414-w4p4iczhl6lli50u
This commit is contained in:
parent
5eaed9dbd4
commit
34965058ec
|
@ -518,15 +518,16 @@ class ir_model_access(osv.osv):
|
||||||
"""
|
"""
|
||||||
assert access_mode in ['read','write','create','unlink'], 'Invalid access mode: %s' % access_mode
|
assert access_mode in ['read','write','create','unlink'], 'Invalid access mode: %s' % access_mode
|
||||||
cr.execute('''SELECT
|
cr.execute('''SELECT
|
||||||
g.name
|
c.name, g.name
|
||||||
FROM
|
FROM
|
||||||
ir_model_access a
|
ir_model_access a
|
||||||
JOIN ir_model m ON (a.model_id=m.id)
|
JOIN ir_model m ON (a.model_id=m.id)
|
||||||
JOIN res_groups g ON (a.group_id=g.id)
|
JOIN res_groups g ON (a.group_id=g.id)
|
||||||
|
LEFT JOIN ir_module_category c ON (c.id=g.category_id)
|
||||||
WHERE
|
WHERE
|
||||||
m.model=%s AND
|
m.model=%s AND
|
||||||
a.perm_''' + access_mode, (model_name,))
|
a.perm_''' + access_mode, (model_name,))
|
||||||
return [x[0] for x in cr.fetchall()]
|
return [('%s/%s' % x) if x[0] else x[1] for x in cr.fetchall()]
|
||||||
|
|
||||||
@tools.ormcache()
|
@tools.ormcache()
|
||||||
def check(self, cr, uid, model, mode='read', raise_exception=True, context=None):
|
def check(self, cr, uid, model, mode='read', raise_exception=True, context=None):
|
||||||
|
@ -570,15 +571,23 @@ class ir_model_access(osv.osv):
|
||||||
r = cr.fetchone()[0]
|
r = cr.fetchone()[0]
|
||||||
|
|
||||||
if not r and raise_exception:
|
if not r and raise_exception:
|
||||||
groups = ', '.join(self.group_names_with_access(cr, model_name, mode)) or '/'
|
groups = '\n\t'.join('- %s' % g for g in self.group_names_with_access(cr, model_name, mode))
|
||||||
msgs = {
|
msg_heads = {
|
||||||
'read': _("You can not read this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
# Messages are declared in extenso so they are properly exported in translation terms
|
||||||
'write': _("You can not write in this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
'read': _("Sorry, you are not allowed to access this document."),
|
||||||
'create': _("You can not create this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
'write': _("Sorry, you are not allowed to modify this document."),
|
||||||
'unlink': _("You can not delete this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
'create': _("Sorry, you are not allowed to create this kind of document."),
|
||||||
|
'unlink': _("Sorry, you are not allowed to delete this document."),
|
||||||
}
|
}
|
||||||
|
if groups:
|
||||||
raise except_orm(_('AccessError'), msgs[mode] % (model_name, groups) )
|
msg_tail = _("Only users with the following access level are currently allowed to do that") + ":\n%s\n\n(" + _("Document model") + ": %s)"
|
||||||
|
msg_params = (groups, model_name)
|
||||||
|
else:
|
||||||
|
msg_tail = _("Please contact your system administrator if you think this is an error.") + "\n\n(" + _("Document model") + ": %s)"
|
||||||
|
msg_params = (model_name,)
|
||||||
|
_logger.warning('Access Denied by ACLs for operation: %s, uid: %s, model: %s', mode, uid, model_name)
|
||||||
|
msg = '%s %s' % (msg_heads[mode], msg_tail)
|
||||||
|
raise except_orm(_('Access Denied'), msg % msg_params)
|
||||||
return r or False
|
return r or False
|
||||||
|
|
||||||
__cache_clearing_methods = []
|
__cache_clearing_methods = []
|
||||||
|
|
|
@ -3487,10 +3487,7 @@ class BaseModel(object):
|
||||||
for sub_ids in cr.split_for_in_conditions(ids):
|
for sub_ids in cr.split_for_in_conditions(ids):
|
||||||
if rule_clause:
|
if rule_clause:
|
||||||
cr.execute(query, [tuple(sub_ids)] + rule_params)
|
cr.execute(query, [tuple(sub_ids)] + rule_params)
|
||||||
if cr.rowcount != len(sub_ids):
|
self._check_record_rules_result_count(cr, user, sub_ids, 'read', context=context)
|
||||||
raise except_orm(_('AccessError'),
|
|
||||||
_('Operation prohibited by access rules, or performed on an already deleted document (Operation: read, Document type: %s).')
|
|
||||||
% (self._description,))
|
|
||||||
else:
|
else:
|
||||||
cr.execute(query, (tuple(sub_ids),))
|
cr.execute(query, (tuple(sub_ids),))
|
||||||
res.extend(cr.dictfetchall())
|
res.extend(cr.dictfetchall())
|
||||||
|
@ -3669,6 +3666,26 @@ class BaseModel(object):
|
||||||
# mention the first one only to keep the error message readable
|
# mention the first one only to keep the error message readable
|
||||||
raise except_orm('ConcurrencyException', _('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0]))
|
raise except_orm('ConcurrencyException', _('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0]))
|
||||||
|
|
||||||
|
def _check_record_rules_result_count(self, cr, uid, ids, operation, context=None):
|
||||||
|
"""Verify that number of returned rows after applying record rules matches
|
||||||
|
the length of `ids`, and raise an appropriate exception if it does not.
|
||||||
|
"""
|
||||||
|
if cr.rowcount != len(ids):
|
||||||
|
# Attempt to distinguish record rule restriction vs deleted records,
|
||||||
|
# to provide a more specific error message
|
||||||
|
cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(ids),))
|
||||||
|
if cr.rowcount != len(ids):
|
||||||
|
if operation == 'unlink':
|
||||||
|
# no need to warn about deleting an already deleted record!
|
||||||
|
return
|
||||||
|
_logger.warning('Failed operation on deleted record(s): %s, uid: %s, model: %s', operation, uid, self._name)
|
||||||
|
raise except_orm(_('Missing document(s)'),
|
||||||
|
_('One of the documents you are trying to access has been deleted, please try again after refreshing.'))
|
||||||
|
_logger.warning('Access Denied by record rules for operation: %s, uid: %s, model: %s', operation, uid, self._name)
|
||||||
|
raise except_orm(_('Access Denied'),
|
||||||
|
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
|
||||||
|
(self._description, operation))
|
||||||
|
|
||||||
def check_access_rights(self, cr, uid, operation, raise_exception=True): # no context on purpose.
|
def check_access_rights(self, cr, uid, operation, raise_exception=True): # no context on purpose.
|
||||||
"""Verifies that the operation given by ``operation`` is allowed for the user
|
"""Verifies that the operation given by ``operation`` is allowed for the user
|
||||||
according to the access rights."""
|
according to the access rights."""
|
||||||
|
@ -3700,7 +3717,7 @@ class BaseModel(object):
|
||||||
if self.is_transient():
|
if self.is_transient():
|
||||||
# Only one single implicit access rule for transient models: owner only!
|
# Only one single implicit access rule for transient models: owner only!
|
||||||
# This is ok to hardcode because we assert that TransientModels always
|
# This is ok to hardcode because we assert that TransientModels always
|
||||||
# have log_access enabled and this the create_uid column is always there.
|
# have log_access enabled so that the create_uid column is always there.
|
||||||
# And even with _inherits, these fields are always present in the local
|
# And even with _inherits, these fields are always present in the local
|
||||||
# table too, so no need for JOINs.
|
# table too, so no need for JOINs.
|
||||||
cr.execute("""SELECT distinct create_uid
|
cr.execute("""SELECT distinct create_uid
|
||||||
|
@ -3708,9 +3725,8 @@ class BaseModel(object):
|
||||||
WHERE id IN %%s""" % self._table, (tuple(ids),))
|
WHERE id IN %%s""" % self._table, (tuple(ids),))
|
||||||
uids = [x[0] for x in cr.fetchall()]
|
uids = [x[0] for x in cr.fetchall()]
|
||||||
if len(uids) != 1 or uids[0] != uid:
|
if len(uids) != 1 or uids[0] != uid:
|
||||||
raise except_orm(_('AccessError'), '%s access is '
|
raise except_orm(_('Access Denied'),
|
||||||
'restricted to your own records for transient models '
|
_('For this kind of document, you may only access records you created yourself.\n\n(Document type: %s)') % (self._description,))
|
||||||
'(except for the super-user).' % operation.capitalize())
|
|
||||||
else:
|
else:
|
||||||
where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
|
where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
|
||||||
if where_clause:
|
if where_clause:
|
||||||
|
@ -3719,10 +3735,7 @@ class BaseModel(object):
|
||||||
cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
|
cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
|
||||||
' WHERE ' + self._table + '.id IN %s' + where_clause,
|
' WHERE ' + self._table + '.id IN %s' + where_clause,
|
||||||
[sub_ids] + where_params)
|
[sub_ids] + where_params)
|
||||||
if cr.rowcount != len(sub_ids):
|
self._check_record_rules_result_count(cr, uid, sub_ids, operation, context=context)
|
||||||
raise except_orm(_('AccessError'),
|
|
||||||
_('Operation prohibited by access rules, or performed on an already deleted document (Operation: %s, Document type: %s).')
|
|
||||||
% (operation, self._description))
|
|
||||||
|
|
||||||
def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
|
def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
|
||||||
"""Call given workflow trigger as a result of a CRUD operation"""
|
"""Call given workflow trigger as a result of a CRUD operation"""
|
||||||
|
|
Loading…
Reference in New Issue