Code

Removed the unnecessary volatiledb and the related complications. Security
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 29 Jul 2002 00:56:06 +0000 (00:56 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 29 Jul 2002 00:56:06 +0000 (00:56 +0000)
much simpler and self-contained now.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@924 57a73879-2fb5-44c3-a270-3262357dd7e2

COPYING.txt
TODO.txt
roundup/security.py
test/test_init.py
test/test_mailgw.py

index db92559a13e33ddb961d4aed5e05707edf3f8a35..f518e8a1753d1a7a7f6ce1bc41311b82e9db8df6 100644 (file)
@@ -1,4 +1,3 @@
-
 Copyright (c) 2002 eKit.com Inc (http://www.ekit.com/)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
index 102cd2efe825b2fa6f177fc549ca4c47c85f5b84..2a0c3069438132a56a90daa000bef97268979882 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -12,6 +12,7 @@ pending  hyperdb: range searching of values (dates in particular)
            [value, value, ...] implies "in"
 pending  hyperdb: make creator, creation and activity available pre-commit
 pending  hyperdb: migrate "id" property to be Number type
+active   hyperdb: modify design document to include all the changes made
 pending  instance: including much simpler upgrade path and the use of
                    non-Python configuration files (ConfigParser)
 pending  instance: cleanup to support config (feature request #498658)
@@ -29,8 +30,7 @@ pending  mailgw: Allow multiple email addresses at one gw with different default
                    roundup: "|roundup-mailgw /instances/dev"
                    vmbugs: "|roundup-mailgw /instances/dev component=voicemail"
 pending  project: switch to a Roundup instance for Roundup bug/feature tracking
-active   security: finish doc/security.txt
-active   security: implement and use the new logical control mechanisms
+active   security: add info from doc/security.txt to design doc
 pending  security: at least an LDAP user database implementation
 pending  security: authenticate over a secure connection
 pending  security: use digital signatures in mailgw
@@ -54,5 +54,6 @@ done hyperdb: fix the journal bloat (RJ)
 done hyperdb: add Boolean and Number types (GM)
 done mailgw: better help message (feature request #558562) (RJ)
 done security: switch to sessions for web authentication (RJ)
+done security: implement and use the new logical control mechanisms
 done web: saving of named queries (GM)
 
index 49e447795014c40af6a5b2c521dca2fa5acfd127..0d50318d524cc635e20ae5abab68f8aad54a2b48 100644 (file)
@@ -1,59 +1,53 @@
 import weakref
 
-from roundup import hyperdb, volatiledb
+from roundup import hyperdb
 
-class PermissionClass(volatiledb.VolatileClass):
-    ''' Include the default attributes:
-        - name (String)
-        - classname (String)
-        - description (String)
+class Permission:
+    ''' Defines a Permission with the attributes
+        - name
+        - description
+        - klass (optional)
 
-        The classname may be unset, indicating that this permission is not
+        The klass may be unset, indicating that this permission is not
         locked to a particular class. That means there may be multiple
         Permissions for the same name for different classes.
     '''
-    def __init__(self, db, classname, **properties):
-        """ set up the default properties
-        """
-        if not properties.has_key('name'):
-            properties['name'] = hyperdb.String()
-        if not properties.has_key('klass'):
-            properties['klass'] = hyperdb.String()
-        if not properties.has_key('description'):
-            properties['description'] = hyperdb.String()
-        volatiledb.VolatileClass.__init__(self, db, classname, **properties)
-
-class RoleClass(volatiledb.VolatileClass):
-    ''' Include the default attributes:
-        - name (String, key)
-        - description (String)
-        - permissions (PermissionClass Multilink)
+    def __init__(self, name='', description='', klass=None):
+        self.name = name
+        self.description = description
+        self.klass = klass
+
+    def __repr__(self):
+        return '<Permission 0x%x %r,%r>'%(id(self), self.name, self.klass)
+
+class Role:
+    ''' Defines a Role with the attributes
+        - name
+        - description
+        - permissions
     '''
-    def __init__(self, db, classname, **properties):
-        """ set up the default properties
-        """
-        if not properties.has_key('name'):
-            properties['name'] = hyperdb.String()
-        if not properties.has_key('description'):
-            properties['description'] = hyperdb.String()
-        if not properties.has_key('permissions'):
-            properties['permissions'] = hyperdb.Multilink('permission')
-        volatiledb.VolatileClass.__init__(self, db, classname, **properties)
-        self.setkey('name')
+    def __init__(self, name='', description='', permissions=None):
+        self.name = name
+        self.description = description
+        if permissions is None:
+            permissions = []
+        self.permissions = permissions
+
+    def __repr__(self):
+        return '<Role 0x%x %r,%r>'%(id(self), self.name, self.permissions)
 
 class Security:
     def __init__(self, db):
         ''' Initialise the permission and role classes, and add in the
             base roles (for admin user).
         '''
-        # use a weak ref to avoid circularity
-        self.db = weakref.proxy(db)
+        self.db = weakref.proxy(db)       # use a weak ref to avoid circularity
 
-        # create the permission class instance (we only need one))
-        self.permission = PermissionClass(db, "permission")
+        # permssions are mapped by name to a list of Permissions by class
+        self.permission = {}
 
-        # create the role class instance (we only need one)
-        self.role = RoleClass(db, "role")
+        # roles are mapped by name to the Role
+        self.role = {}
 
         # the default Roles
         self.addRole(name="User", description="A regular user, no privs")
@@ -82,22 +76,19 @@ class Security:
 
             Raise ValueError if there is no exact match.
         '''
-        perm = self.db.permission
-        for permissionid in perm.stringFind(name=permission):
-            klass = perm.get(permissionid, 'klass')
-            if classname is not None and classname == klass:
-                return permissionid
-            elif not classname and not klass:
-                return permissionid
-        if not classname:
+        if not self.permission.has_key(permission):
             raise ValueError, 'No permission "%s" defined'%permission
+        for perm in self.permission[permission]:
+            if perm.klass is not None and perm.klass == classname:
+                return perm
+            elif not perm.klass and not classname:
+                return perm
         raise ValueError, 'No permission "%s" defined for "%s"'%(permission,
             classname)
 
     def hasPermission(self, permission, userid, classname=None):
         ''' Look through all the Roles, and hence Permissions, and see if
             "permission" is there for the specified classname.
-
         '''
         roles = self.db.user.get(userid, 'roles')
         if roles is None:
@@ -105,12 +96,8 @@ class Security:
         for rolename in roles.split(','):
             if not rolename:
                 continue
-            roleid = self.db.role.lookup(rolename)
-            for permissionid in self.db.role.get(roleid, 'permissions'):
-                if self.db.permission.get(permissionid, 'name') != permission:
-                    continue
-                klass = self.db.permission.get(permissionid, 'klass')
-                if klass is None or klass == classname:
+            for perm in self.role[rolename].permissions:
+                if perm.klass is None or perm.klass == classname:
                     return 1
         return 0
 
@@ -142,20 +129,22 @@ class Security:
         ''' Create a new Permission with the properties defined in
             'propspec'
         '''
-        return self.db.permission.create(**propspec)
+        perm = Permission(**propspec)
+        self.permission.setdefault(perm.name, []).append(perm)
+        return perm
 
     def addRole(self, **propspec):
         ''' Create a new Role with the properties defined in 'propspec'
         '''
-        return self.db.role.create(**propspec)
+        role = Role(**propspec)
+        self.role[role.name] = role
+        return role
 
-    def addPermissionToRole(self, rolename, permissionid):
+    def addPermissionToRole(self, rolename, permission):
         ''' Add the permission to the role's permission list.
 
-            'rolename' is the name of the role to add 'permissionid'.
+            'rolename' is the name of the role to add the permission to.
         '''
-        roleid = self.db.role.lookup(rolename)
-        permissions = self.db.role.get(roleid, 'permissions')
-        permissions.append(permissionid)
-        self.db.role.set(roleid, permissions=permissions)
+        role = self.role[rolename]
+        role.permissions.append(permission)
 
index 11e0aeb58413588bb5b75524ce1a316c3af494a0..2d454cd213e646ef72df3a8eb7f1b3f74918f5b6 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_init.py,v 1.14 2002-07-26 08:27:00 richard Exp $
+# $Id: test_init.py,v 1.15 2002-07-29 00:56:06 richard Exp $
 
 import unittest, os, shutil, errno, imp, sys
 
@@ -91,7 +91,7 @@ class ExtendedTestCase(MyTestCase):
         l = db.keyword.list()
         ae(l, [])
         l = db.user.list()
-        ae(l, ['1'])
+        ae(l, ['1', '2'])
         l = db.msg.list()
         ae(l, [])
         l = db.file.list()
@@ -155,6 +155,11 @@ def suite():
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.14  2002/07/26 08:27:00  richard
+# Very close now. The cgi and mailgw now use the new security API. The two
+# templates have been migrated to that setup. Lots of unit tests. Still some
+# issue in the web form for editing Roles assigned to users.
+#
 # Revision 1.13  2002/07/14 02:05:54  richard
 # . all storage-specific code (ie. backend) is now implemented by the backends
 #
index 524f85034749a41cffd2a4e5b94658eb387d72a3..6d531977479a332e96b65d867b3148df37480f8d 100644 (file)
@@ -8,7 +8,7 @@
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 #
-# $Id: test_mailgw.py,v 1.24 2002-07-26 08:27:00 richard Exp $
+# $Id: test_mailgw.py,v 1.25 2002-07-29 00:56:06 richard Exp $
 
 import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib
 
@@ -633,8 +633,8 @@ Subject: [issue1] Testing... [nosy=-richard]
 
     def testNewUserAuthor(self):
         # first without the permission
-        Anonid = self.db.role.lookup('Anonymous')
-        self.db.role.set(Anonid, permissions=[])
+        # heh... just ignore the API for a second ;)
+        self.db.security.role['Anonymous'].permissions=[]
         anonid = self.db.user.lookup('anonymous')
         self.db.user.set(anonid, roles='Anonymous')
 
@@ -660,7 +660,7 @@ This is a test submission of a new issue.
 
         # now with the permission
         p = self.db.security.getPermission('Email Registration')
-        self.db.role.set(Anonid, permissions=[p])
+        self.db.security.role['Anonymous'].permissions=[p]
         handler = self.instance.MailGW(self.instance, self.db)
         handler.trapExceptions = 0
         message = cStringIO.StringIO(s)
@@ -781,6 +781,11 @@ def suite():
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.24  2002/07/26 08:27:00  richard
+# Very close now. The cgi and mailgw now use the new security API. The two
+# templates have been migrated to that setup. Lots of unit tests. Still some
+# issue in the web form for editing Roles assigned to users.
+#
 # Revision 1.23  2002/07/14 02:02:43  richard
 # Fixed the unit tests for the new multilist controls in the mailgw
 #