* improve security on attachment: use the access right of the linked model

* improve cache managment

bzr revid: christophe@tinyerp.com-20081016182816-o2h4swwye1urt3kw
This commit is contained in:
Christophe Simonis 2008-10-16 20:28:16 +02:00
parent 05543e0af7
commit f538cefe02
4 changed files with 107 additions and 9 deletions

View File

@ -29,8 +29,72 @@
##############################################################################
from osv import fields,osv
from osv.orm import except_orm
import tools
class ir_attachment(osv.osv):
def check(self, cr, uid, ids, mode):
if not ids:
return
ima = self.pool.get('ir.model.access')
if isinstance(ids, (int, long)):
ids = [ids]
objs = self.browse(cr, uid, ids) or []
for o in objs:
if o and o.res_model:
ima.check(cr, uid, o.res_model, mode)
check = tools.cache()(check)
def search(self, cr, uid, *args, **kwargs):
ids = super(ir_attachment, self).search(cr, uid, *args, **kwargs)
if not ids:
return []
models = super(ir_attachment,self).read(cr, uid, ids, ['id', 'res_model'])
cache = {}
ima = self.pool.get('ir.model.access')
for m in models:
if m['res_model'] in cache:
if not cache[m['res_model']]:
ids.remove(m['id'])
continue
cache[m['res_model']] = ima.check(cr, uid, m['res_model'], 'read', raise_exception=False)
return ids
def read(self, cr, uid, ids, *args, **kwargs):
self.check(cr, uid, ids, 'read')
return super(ir_attachment, self).read(cr, uid, ids, *args, **kwargs)
def write(self, cr, uid, ids, *args, **kwargs):
self.check(cr, uid, ids, 'write')
return super(ir_attachment, self).write(cr, uid, ids, *args, **kwargs)
def copy(self, cr, uid, id, *args, **kwargs):
self.check(cr, uid, [id], 'write')
return super(ir_attachment, self).copy(cr, uid, id, *args, **kwargs)
def unlink(self, cr, uid, ids, *args, **kwargs):
self.check(cr, uid, ids, 'unlink')
return super(ir_attachment, self).unlink(cr, uid, ids, *args, **kwargs)
def create(self, cr, uid, values, *args, **kwargs):
if 'res_model' in values and values['res_model'] != '':
self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create')
return super(ir_attachment, self).create(cr, uid, values, *args, **kwargs)
def clear_cache(self):
self.check()
def __init__(self, *args, **kwargs):
r = super(ir_attachment, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
return r
def __del__(self):
self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache')
return super(ir_attachment, self).__del__()
_name = 'ir.attachment'
_columns = {
'name': fields.char('Attachment Name',size=64, required=True),

View File

@ -329,27 +329,42 @@ class ir_model_access(osv.osv):
check = tools.cache()(check)
__cache_clearing_methods = []
def register_cache_clearing_method(self, model, method):
self.__cache_clearing_methods.append((model, method))
def unregister_cache_clearing_method(self, model, method):
try:
i = self.__cache_clearing_methods.index((model, method))
del self.__cache_clearing_methods[i]
except ValueError:
pass
def call_cache_clearing_methods(self):
for model, method in self.__cache_clearing_methods:
getattr(self.pool.get(model), method)()
#
# Check rights on actions
#
def write(self, cr, uid, *args, **argv):
self.pool.get('ir.ui.menu').clear_cache()
self.call_cache_clearing_methods()
res = super(ir_model_access, self).write(cr, uid, *args, **argv)
self.check()
self.check() # clear the cache of check function
return res
def create(self, cr, uid, *args, **argv):
res = super(ir_model_access, self).create(cr, uid, *args, **argv)
self.check()
return res
def unlink(self, cr, uid, *args, **argv):
self.pool.get('ir.ui.menu').clear_cache()
self.call_cache_clearing_methods()
res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
self.check()
return res
def read(self, cr, uid, *args, **argv):
res = super(ir_model_access, self).read(cr, uid, *args, **argv)
self.check()
return res
ir_model_access()
class ir_model_data(osv.osv):

View File

@ -61,7 +61,13 @@ class ir_ui_menu(osv.osv):
def __init__(self, *args, **kwargs):
self._cache = {}
return super(ir_ui_menu, self).__init__(*args, **kwargs)
r = super(ir_ui_menu, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
return r
def __del__(self):
self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache')
return super(ir_ui_menu, self).__del__()
def clear_cache(self):
# radical but this doesn't frequently happen

View File

@ -589,6 +589,13 @@ class currency(float):
# return str(display_value)
def is_hashable(h):
try:
hash(h)
return True
except TypeError:
return False
#
# Use it as a decorator of the function you plan to cache
# Timeout: 0 = no timeout, otherwise in seconds
@ -607,6 +614,11 @@ class cache(object):
# Update named arguments with positional argument values
kwargs.update(dict(zip(arg_names, args)))
for k in kwargs:
if isinstance(kwargs[k], (list, dict, set)):
kwargs[k] = tuple(kwargs[k])
elif not is_hashable(kwargs[k]):
kwargs[k] = repr(kwargs[k])
kwargs = kwargs.items()
kwargs.sort()
@ -621,7 +633,8 @@ class cache(object):
return value
# Work out new value, cache it and return it
# Should copy() this value to avoid futur modf of the cacle ?
# FIXME Should copy() this value to avoid futur modifications of the cache ?
# FIXME What about exceptions ?
result = fn(self2,cr,**dict(kwargs))
self.cache[key] = (result, time.time())