diff --git a/CHANGELOG b/CHANGELOG index c87d99f6b7..6c5ed833f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Allow client to manage model access * Add time format validation * Remove ModelSQL.default_sequence diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 67ff4ffb17..99000a9940 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -49,6 +49,9 @@ class Model(ModelSQL, ModelView): '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 @@ class Model(ModelSQL, ModelView): 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 @@ class ModelAccess(ModelSQL, ModelView): '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 @@ class ModelAccess(ModelSQL, ModelView): 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: diff --git a/trytond/model/model.py b/trytond/model/model.py index 5d2df7a37d..6e03887b31 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -419,13 +419,10 @@ class Model(WarningErrorMixin, URLMixin): 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 @@ class Model(WarningErrorMixin, URLMixin): 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']: