[IMP] osv_memory: disabled regular ir_model_access access rights and switched to...
authorOlivier Dony <odo@openerp.com>
Thu, 24 Jun 2010 15:56:02 +0000 (17:56 +0200)
committerOlivier Dony <odo@openerp.com>
Thu, 24 Jun 2010 15:56:02 +0000 (17:56 +0200)
bzr revid: odo@openerp.com-20100624155602-noiat5i91uyeyy5t

bin/addons/__init__.py
bin/addons/base/ir/ir.xml
bin/addons/base/ir/ir_model.py
bin/osv/expression.py
bin/osv/orm.py

index a538e92..6cccbb9 100644 (file)
@@ -33,8 +33,6 @@ import pooler
 
 
 import netsvc
-from osv import fields
-import addons
 
 import zipfile
 import release
@@ -303,7 +301,7 @@ def load_information_from_description_file(module):
     :param module: The name of the module (sale, purchase, ...)
     """
     for filename in ['__openerp__.py', '__terp__.py']:
-        description_file = addons.get_module_resource(module, filename)
+        description_file = get_module_resource(module, filename)
         if os.path.isfile(description_file):
             return eval(tools.file_open(description_file).read())
 
@@ -823,7 +821,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
         while True:
             loop_guardrail += 1
             if loop_guardrail > 100:
-                raise ProgrammingError()
+                raise ValueError('Possible recursive module tree detected, aborting.')
             cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
 
             module_list = [name for (name,) in cr.fetchall() if name not in graph]
@@ -840,9 +838,19 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
             has_updates = has_updates or r
 
         if has_updates:
-            cr.execute("""select model,name from ir_model where id NOT IN (select model_id from ir_model_access)""")
+            cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
             for (model, name) in cr.fetchall():
-                logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
+                model_obj = pool.get(model)
+                if not isinstance(model_obj, osv.osv.osv_memory):
+                    logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
+
+            # Temporary warning while we remove access rights on osv_memory objects, as they have
+            # been replaced by owner-only access rights
+            cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""")
+            for (model, name) in cr.fetchall():
+                model_obj = pool.get(model)
+                if isinstance(model_obj, osv.osv.osv_memory):
+                    logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name))
 
             cr.execute("SELECT model from ir_model")
             for (model,) in cr.fetchall():
index d5be953..2949107 100644 (file)
                         <page string="Object">
                             <field name="name" select="1"/>
                             <field name="model" select="1"/>
+                            <field name="osv_memory" select="1"/>
                             <separator colspan="4" string="Fields"/>
                             <field colspan="4" context="{'manual':True}" name="field_id" nolabel="1">
                                 <tree string="Fields Description">
index 086a3c3..eb6959a 100644 (file)
@@ -44,6 +44,25 @@ class ir_model(osv.osv):
     _name = 'ir.model'
     _description = "Objects"
     _rec_name = 'name'
+
+    def _is_osv_memory(self, cr, uid, ids, field_name, arg, context=None):
+        models = self.browse(cr, uid, ids, context=context)
+        res = dict.fromkeys(ids)
+        for model in models:
+            res[model.id] = isinstance(self.pool.get(model.model), osv.osv_memory)
+        return res
+
+    def _search_osv_memory(self, cr, uid, model, name, domain, context=None):
+        if not domain:
+            return []
+        field, operator, value = domain[0]
+        if operator not in ['=', '!=']:
+            raise osv.except_osv('Invalid search criterions','The osv_memory field can only be compared with = and != operator.')
+        value = bool(value) if operator == '=' else not bool(value)
+        all_model_ids = self.search(cr, uid, [], context=context)
+        is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context)
+        return [('id', 'in', [id for id in is_osv_mem if bool(is_osv_mem[id]) == value])]
+
     _columns = {
         'name': fields.char('Object Name', size=64, translate=True, required=True),
         'model': fields.char('Object', size=64, required=True, select=1),
@@ -51,6 +70,9 @@ class ir_model(osv.osv):
         'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
         'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Manually Created',readonly=True),
         'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
+        'osv_memory': fields.function(_is_osv_memory, method=True, string='In-memory model', type='boolean',
+            fnct_search=_search_osv_memory,
+            help="Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory)")
     }
     _defaults = {
         'model': lambda *a: 'x_',
@@ -81,7 +103,7 @@ class ir_model(osv.osv):
 
     def unlink(self, cr, user, ids, context=None):
         for model in self.browse(cr, user, ids, context):
-            if model.state <> 'manual':
+            if model.state != 'manual':
                 raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
         res = super(ir_model, self).unlink(cr, user, ids, context)
         pooler.restart_pool(cr.dbname)
@@ -300,7 +322,7 @@ class ir_model_access(osv.osv):
     _name = 'ir.model.access'
     _columns = {
         'name': fields.char('Name', size=64, required=True),
-        'model_id': fields.many2one('ir.model', 'Object', required=True),
+        'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)]),
         'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade'),
         'perm_read': fields.boolean('Read Access'),
         'perm_write': fields.boolean('Write Access'),
index 5fbdd84..15e8d86 100644 (file)
@@ -76,6 +76,9 @@ class expression(object):
         self.__main_table = None # 'root' table. set by parse()
         self.__DUMMY_LEAF = (1, '=', 1) # a dummy leaf that must not be parsed or sql generated
 
+    @property
+    def exp(self):
+        return self.__exp[:]
 
     def parse(self, cr, uid, table, context):
         """ transform the leafs of the expression """
index e365949..734bd41 100644 (file)
@@ -1754,6 +1754,10 @@ class orm_memory(orm_template):
         self.check_id = 0
         cr.execute('delete from wkf_instance where res_type=%s', (self._name,))
 
+    def _check_access(self, uid, object_id, mode):
+        if uid != 1 and self.datas[object_id]['internal.create_uid'] != uid:
+            raise except_orm(_('AccessError'), '%s access is only allowed on your own records for osv_memory objects' % mode.capitalize())
+
     def vaccum(self, cr, uid):
         self.check_id += 1
         if self.check_id % self._check_time:
@@ -1774,7 +1778,6 @@ class orm_memory(orm_template):
     def read(self, cr, user, ids, fields_to_read=None, context=None, load='_classic_read'):
         if not context:
             context = {}
-        self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
         if not fields_to_read:
             fields_to_read = self._columns.keys()
         result = []
@@ -1785,8 +1788,10 @@ class orm_memory(orm_template):
             for id in ids:
                 r = {'id': id}
                 for f in fields_to_read:
-                    if id in self.datas:
-                        r[f] = self.datas[id].get(f, False)
+                    record = self.datas.get(id)
+                    if record:
+                        self._check_access(user, id, 'read')
+                        r[f] = record.get(f, False)
                         if r[f] and isinstance(self._columns[f], fields.binary) and context.get('bin_size', False):
                             r[f] = len(r[f])
                 result.append(r)
@@ -1804,7 +1809,6 @@ class orm_memory(orm_template):
     def write(self, cr, user, ids, vals, context=None):
         if not ids:
             return True
-        self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context)
         vals2 = {}
         upd_todo = []
         for field in vals:
@@ -1812,18 +1816,18 @@ class orm_memory(orm_template):
                 vals2[field] = vals[field]
             else:
                 upd_todo.append(field)
-        for id_new in ids:
-            self.datas[id_new].update(vals2)
-            self.datas[id_new]['internal.date_access'] = time.time()
+        for object_id in ids:
+            self._check_access(user, object_id, mode='write')
+            self.datas[object_id].update(vals2)
+            self.datas[object_id]['internal.date_access'] = time.time()
             for field in upd_todo:
-                self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
-        self._validate(cr, user, [id_new], context)
+                self._columns[field].set_memory(cr, self, object_id, field, vals[field], user, context)
+        self._validate(cr, user, [object_id], context)
         wf_service = netsvc.LocalService("workflow")
-        wf_service.trg_write(user, self._name, id_new, cr)
-        return id_new
+        wf_service.trg_write(user, self._name, object_id, cr)
+        return object_id
 
     def create(self, cr, user, vals, context=None):
-        self.pool.get('ir.model.access').check(cr, user, self._name, 'create', context=context)
         self.vaccum(cr, user)
         self.next_id += 1
         id_new = self.next_id
@@ -1842,6 +1846,7 @@ class orm_memory(orm_template):
                 upd_todo.append(field)
         self.datas[id_new] = vals2
         self.datas[id_new]['internal.date_access'] = time.time()
+        self.datas[id_new]['internal.create_uid'] = user
 
         for field in upd_todo:
             self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
@@ -1936,13 +1941,19 @@ class orm_memory(orm_template):
             import expression
             e = expression.expression(args)
             e.parse(cr, user, self, context)
-            res=e.__dict__['_expression__exp']
+            res = e.exp
         return res or []
 
     def search(self, cr, user, args, offset=0, limit=None, order=None,
             context=None, count=False):
         if not context:
             context = {}
+
+        # implicit filter on current user
+        if not args:
+            args = []
+        args.insert(0, ('internal.create_uid', '=', user))
+
         result = self._where_calc(cr, user, args, context=context)
         if result==[]:
             return self.datas.keys()
@@ -1954,25 +1965,20 @@ class orm_memory(orm_template):
         if result:
             for id, data in self.datas.items():
                 counter=counter+1
-                data['id']  = id
-                if limit and (counter >int(limit)):
+                data['id'] = id
+                if limit and (counter > int(limit)):
                     break
                 f = True
                 for arg in result:
-                     if arg[1] =='=':
-                         val =eval('data[arg[0]]'+'==' +' arg[2]', locals())
-                     elif arg[1] in ['<','>','in','not in','<=','>=','<>']:
-                         val =eval('data[arg[0]]'+arg[1] +' arg[2]', locals())
-                     elif arg[1] in ['ilike']:
-                         if str(data[arg[0]]).find(str(arg[2]))!=-1:
-                             val= True
-                         else:
-                             val=False
-
-                     if f and val:
-                         f = True
-                     else:
-                         f = False
+                    if arg[1] == '=':
+                        val = eval('data[arg[0]]'+'==' +' arg[2]', locals())
+                    elif arg[1] in ['<','>','in','not in','<=','>=','<>']:
+                        val = eval('data[arg[0]]'+arg[1] +' arg[2]', locals())
+                    elif arg[1] in ['ilike']:
+                        val = (str(data[arg[0]]).find(str(arg[2]))!=-1)
+
+                    f = f and val
+
                 if f:
                     res.append(id)
         if count:
@@ -1980,20 +1986,22 @@ class orm_memory(orm_template):
         return res or []
 
     def unlink(self, cr, uid, ids, context=None):
-        self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink', context=context)
         for id in ids:
-            if id in self.datas:
-                del self.datas[id]
+            self._check_access(uid, id, 'unlink')
+            self.datas.pop(id, None)
         if len(ids):
             cr.execute('delete from wkf_instance where res_type=%s and res_id IN %s', (self._name, tuple(ids)))
         return True
 
     def perm_read(self, cr, user, ids, context=None, details=True):
         result = []
+        credentials = self.pool.get('res.users').name_get(cr, user, [user])[0]
+        create_date = time.strftime('%Y-%m-%d %H:%M:%S')
         for id in ids:
+            self._check_access(user, id, 'read')
             result.append({
-                'create_uid': (user, 'Root'),
-                'create_date': time.strftime('%Y-%m-%d %H:%M:%S'),
+                'create_uid': credentials,
+                'create_date': create_date,
                 'write_uid': False,
                 'write_date': False,
                 'id': id