Reviewers: ,
Please review this at http://codereview.tryton.org/346001/
Affected files:
M CHANGELOG
M trytond/ir/model.py
M trytond/model/model.py
Index: CHANGELOG
===================================================================
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Allow client to manage model access
+
Version 2.4.0 - 2012-04-23
* Bug fixes (see mercurial logs for details)
* Don't allow rpc call on ModelStorage without ModelView (CVE-2012-0215)
Index: trytond/ir/model.py
===================================================================
--- a/trytond/ir/model.py
+++ b/trytond/ir/model.py
@@ -49,6 +49,9 @@
'invalid_module': 'Module Name must be a python identifier!',
})
self._order.insert(0, ('model', 'ASC'))
+ self._rpc.update({
+ 'list_models': False,
+ })
def check_module(self, ids):
'''
@@ -59,6 +62,17 @@
return False
return True
+ def list_models(self):
+ 'Return a list of all models names'
+ with Transaction().set_user(0):
+ model_ids = self.search([], order=[
+ ('module', 'ASC'), # Optimization assumption
+ ('model', 'ASC'),
+ ('id', 'ASC'),
+ ])
+ models = self.browse(model_ids)
+ return [m.model for m in models]
+
def create(self, vals):
pool = Pool()
property_obj = pool.get('ir.property')
@@ -264,6 +278,9 @@
'create': 'You can not create this kind of document! (%s)',
'delete': 'You can not delete this document! (%s)',
})
+ self._rpc.update({
+ 'get_access': False,
+ })
def check_xml_record(self, ids, values):
return True
@@ -280,38 +297,45 @@
def default_perm_delete(self):
return False
+ def get_access(self, models):
+ 'Return access for models'
+ pool = Pool()
+ ir_model_obj = pool.get('ir.model')
+ user_group_obj = pool.get('res.user-res.group')
+ cursor = Transaction().cursor
+ default = {'read': True, 'write': True, 'create': True, 'delete':
True}
+ access = dict((m, default) for m in models)
+ cursor.execute(('SELECT '
+ 'm.model, '
+ 'MAX(CASE WHEN a.perm_read THEN 1 ELSE 0 END), '
+ 'MAX(CASE WHEN a.perm_write THEN 1 ELSE 0 END), '
+ 'MAX(CASE WHEN a.perm_create THEN 1 ELSE 0 END), '
+ 'MAX(CASE WHEN a.perm_delete THEN 1 ELSE 0 END) '
+ 'FROM "%s" AS a '
+ 'JOIN "%s" AS m '
+ 'ON (a.model = m.id) '
+ 'LEFT JOIN "%s" AS gu '
+ 'ON (gu."group" = a."group") '
+ 'WHERE m.model IN (' + ','.join(('%%s',) * len(models))
+ ') '
+ 'AND (gu."user" = %%s OR a."group" IS NULL) '
+ 'GROUP BY m.model')
+ % (self._table, ir_model_obj._table, user_group_obj._table),
+ list(models) + [Transaction().user])
+ access.update(dict(
+ (m, {'read': r, 'write': w, 'create': c, 'delete': d})
+ for m, r, w, c, d in cursor.fetchall()))
+ return access
+
@Cache('ir_model_access.check')
def check(self, model_name, mode='read', raise_exception=True):
- '''
- Check access for model_name
-
- :param model_name: the model name
- :param mode: 'read', 'write', 'create' or 'delete'
- :param raise_exception: raise an exception if the test failed
-
- :return: a boolean
- '''
+ 'Check access for model_name and mode'
assert mode in ['read', 'write', 'create', 'delete'], \
'Invalid access mode for security'
if Transaction().user == 0:
return True
- pool = Pool()
- ir_model_obj = pool.get('ir.model')
- user_group_obj = pool.get('res.user-res.group')
- cursor = Transaction().cursor
-
- cursor.execute('SELECT MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
- 'FROM "%s" AS a '
- 'JOIN "%s" AS m '
- 'ON (a.model = m.id) '
- 'LEFT JOIN "%s" AS gu '
- 'ON (gu."group" = a."group") '
- 'WHERE m.model = %%s AND (gu."user" = %%s OR a."group" IS
NULL)'
- % (mode, self._table, ir_model_obj._table,
user_group_obj._table),
- (model_name, Transaction().user))
- access, = cursor.fetchone()
- if not access and access is not None:
+ access = self.get_access([model_name])[model_name][mode]
+ if not access:
if raise_exception:
self.raise_user_error(mode, model_name)
else:
Index: trytond/model/model.py
===================================================================
--- a/trytond/model/model.py
+++ b/trytond/model/model.py
@@ -419,13 +419,10 @@
res = {}
pool = Pool()
translation_obj = pool.get('ir.translation')
- model_access_obj = pool.get('ir.model.access')
field_access_obj = pool.get('ir.model.field.access')
for parent in self._inherits:
res.update(pool.get(parent).fields_get(fields_names))
- write_access = model_access_obj.check(self._name, 'write',
- raise_exception=False)
#Add translation to cache
language = Transaction().language
@@ -480,7 +477,7 @@
if getattr(self._columns[field], arg, None) is not None:
res[field][arg] =
copy.copy(getattr(self._columns[field],
arg))
- if not write_access or not fwrite_accesses.get(field, True):
+ if not fwrite_accesses.get(field, True):
res[field]['readonly'] = True
if res[field].get('states') and \
'readonly' in res[field]['states']:
--
[email protected] mailing list