Code

python2.3 compatibility fixes
[roundup.git] / roundup / security.py
index 3dfa8bd442c13ec068ca90589a10e55fc9f5b9fa..e3841ba298508776a4667d7affdc08066152e655 100644 (file)
@@ -54,6 +54,28 @@ class Permission:
         # we have a winner
         return 1
 
+    def searchable(self, classname, property):
+        """ A Permission is searchable for the given permission if it
+            doesn't include a check method and otherwise matches the
+            given parameters.
+        """
+        if self.name not in ('View', 'Search'):
+            return 0
+
+        # are we checking the correct class
+        if self.klass is not None and self.klass != classname:
+            return 0
+
+        # what about property?
+        if not self._properties_dict[property]:
+            return 0
+
+        if self.check:
+            return 0
+
+        return 1
+
+
     def __repr__(self):
         return '<Permission 0x%x %r,%r,%r,%r>'%(id(self), self.name,
             self.klass, self.properties, self.check)
@@ -175,6 +197,81 @@ class Security:
                     return 1
         return 0
 
+    def roleHasSearchPermission(self, classname, property, *rolenames):
+        """ For each of the given roles, check the permissions.
+            Property can be a transitive property.
+        """
+        perms = []
+        # pre-compute permissions
+        for rn in rolenames :
+            for perm in self.role[rn].permissions:
+                perms.append(perm)
+        # Note: break from inner loop means "found"
+        #       break from outer loop means "not found"
+        cn = classname
+        prev = None
+        prop = None
+        Link = hyperdb.Link
+        Multilink = hyperdb.Multilink
+        for propname in property.split('.'):
+            if prev:
+                try:
+                    cn = prop.classname
+                except AttributeError:
+                    break
+            prev = propname
+            try:
+                cls = self.db.getclass(cn)
+                prop = cls.getprops()[propname]
+            except KeyError:
+                break
+            for perm in perms:
+                if perm.searchable(cn, propname):
+                    break
+            else:
+                break
+        else:
+            # for Link and Multilink require search permission on label-
+            # and order-properties and on ID
+            if isinstance(prop, Multilink) or isinstance(prop, Link):
+                try:
+                    cls = self.db.getclass(prop.classname)
+                except KeyError:
+                    return 0
+                props = dict.fromkeys(('id', cls.labelprop(), cls.orderprop()))
+                for p in props.iterkeys():
+                    for perm in perms:
+                        if perm.searchable(prop.classname, p):
+                            break
+                    else:
+                        return 0
+            return 1
+        return 0
+
+    def hasSearchPermission(self, userid, classname, property):
+        '''Look through all the Roles, and hence Permissions, and
+           see if "permission" exists given the constraints of
+           classname and property.
+
+           A search permission is granted if we find a 'View' or
+           'Search' permission for the user which does *not* include
+           a check function. If such a permission is found, the user may
+           search for the given property in the given class.
+
+           Note that classname *and* property are mandatory arguments.
+
+           Contrary to hasPermission, the search will *not* match if
+           there are additional constraints (namely a search function)
+           on a Permission found.
+
+           Concerning property, the Permission matched must have
+           either no properties listed or the property must appear in
+           the list.
+        '''
+        roles = [r for r in self.db.user.get_roles(userid)
+                 if r and self.role.has_key(r)]
+        return self.roleHasSearchPermission (classname, property, *roles)
+
     def addPermission(self, **propspec):
         ''' Create a new Permission with the properties defined in
             'propspec'. See the Permission class for the possible
@@ -208,4 +305,22 @@ class Security:
         role = self.role[rolename.lower()]
         role.permissions.append(permission)
 
+    # Convenience methods for removing non-allowed properties from a
+    # filterspec or sort/group list
+
+    def filterFilterspec(self, userid, classname, filterspec):
+        """ Return a filterspec that has all non-allowed properties removed.
+        """
+        return dict ([(k, v) for k, v in filterspec.iteritems()
+            if self.hasSearchPermission(userid,classname,k)])
+
+    def filterSortspec(self, userid, classname, sort):
+        """ Return a sort- or group-list that has all non-allowed properties
+            removed.
+        """
+        if isinstance(sort, tuple) and sort[0] in '+-':
+            sort = [sort]
+        return [(d, p) for d, p in sort
+            if self.hasSearchPermission(userid,classname,p)]
+
 # vim: set filetype=python sts=4 sw=4 et si :