diff -r cfa7d43a3658 roundup/security.py --- a/roundup/security.py Mon Jan 20 16:32:39 2025 -0500 +++ b/roundup/security.py Tue Jan 21 14:19:29 2025 -0500 @@ -2,13 +2,80 @@ """ __docformat__ = 'restructuredtext' +import logging import weakref +from functools import wraps from roundup import hyperdb, support -import logging logger = logging.getLogger('roundup.security') +EnableMemoize = True + + +class Memoize: + cache = {} + + def __init__(self, logging=True): + self.logging = logging + + def memoize_hasPermission(self, p): + + @wraps(p) + def inner(s, permission, userid, classname=None, property=None, + itemid=None, skip_permissions_with_check=False): + '''Only cache simple calls permission/username/classname. + if prop or itemid defined call the underlying method. + ''' + + logging_tuple = (permission, userid, classname, property, + itemid, skip_permissions_with_check) + cache_key_tuple = (permission, userid, classname, + skip_permissions_with_check) + + log = logger.error + + if property or itemid: + if self.logging is True: + log('SKIP: memoize, %s', logging_tuple) + return p(s, permission, userid, classname, property, + itemid, skip_permissions_with_check) + + if cache_key_tuple in self.cache: + if self.logging is True: + cache_val = self.cache[cache_key_tuple] + log('HIT: returning memoize, %s = %s', + logging_tuple, cache_val) + log('CACHESIZE: id = %s, len = %s cache = %s', + id(s), len(self.cache), self.cache) + return cache_val + + r = p(s, permission, userid, classname, property, itemid, + skip_permissions_with_check) + self.cache[cache_key_tuple] = r + if self.logging is True: + log('MISS: storing %s = %s', logging_tuple, r) + return r + + return inner + + def size(self): + return len(self.cache) + + def clear(self, permission_args=None): + """Call like: + + .clear(("Web Access", "2", None)) + + to clear web access permmission in cache. + """ + if not permission_args: + self.cache.clear() + return + + if permission_args in self.cache: + del self.cache[permission_args] + class Permission: ''' Defines a Permission with the attributes @@ -382,6 +449,9 @@ raise ValueError('No permission "%s" defined for "%s"' % (permission, classname)) + cache_hasPermission = Memoize(logging=True) + + @cache_hasPermission.memoize_hasPermission def hasPermission(self, permission, userid, classname=None, property=None, itemid=None, skip_permissions_with_check=False): diff -r cfa7d43a3658 test/test_mailgw.py --- a/test/test_mailgw.py Mon Jan 20 16:32:39 2025 -0500 +++ b/test/test_mailgw.py Tue Jan 21 14:19:29 2025 -0500 @@ -2892,6 +2892,8 @@ self.db.security.getPermission('Web Access', None), ] self.db.security.role['anonymous'].addPermission(*p) + + self.db.security.cache_hasPermission.clear() try: self._handle_mail(message) except Unauthorized as value: @@ -2919,6 +2921,9 @@ self.db.security.getPermission('Email Access', None), ] self.db.security.role['anonymous'].addPermission(*p) + + self.db.security.cache_hasPermission.clear() + self._handle_mail(message) m = self.db.user.list() m.sort()