From d20250cd9b34cc0a4440034327f33b5f655c233f Mon Sep 17 00:00:00 2001 From: richard Date: Mon, 29 Jul 2002 00:56:06 +0000 Subject: [PATCH] Removed the unnecessary volatiledb and the related complications. Security 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 | 1 - TODO.txt | 5 +- roundup/security.py | 115 ++++++++++++++++++++------------------------ test/test_init.py | 9 +++- test/test_mailgw.py | 13 +++-- 5 files changed, 71 insertions(+), 72 deletions(-) diff --git a/COPYING.txt b/COPYING.txt index db92559..f518e8a 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -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 diff --git a/TODO.txt b/TODO.txt index 102cd2e..2a0c306 100644 --- 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) diff --git a/roundup/security.py b/roundup/security.py index 49e4477..0d50318 100644 --- a/roundup/security.py +++ b/roundup/security.py @@ -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 ''%(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 ''%(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) diff --git a/test/test_init.py b/test/test_init.py index 11e0aeb..2d454cd 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -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 # diff --git a/test/test_mailgw.py b/test/test_mailgw.py index 524f850..6d53197 100644 --- a/test/test_mailgw.py +++ b/test/test_mailgw.py @@ -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 # -- 2.30.2