1 """Handle the security declarations used in Roundup trackers.
3 __docformat__
= 'restructuredtext'
7 from roundup
import hyperdb
, support
10 ''' Defines a Permission with the attributes
14 - properties (optional)
15 - check function (optional)
17 The klass may be unset, indicating that this permission is not
18 locked to a particular class. That means there may be multiple
19 Permissions for the same name for different classes.
21 If property names are set, permission is restricted to those
24 If check function is set, permission is granted only when
25 the function returns value interpreted as boolean true.
26 The function is called with arguments db, userid, itemid.
28 def __init__(self
, name
='', description
='', klass
=None,
29 properties
=None, check
=None):
31 self
.description
= description
33 self
.properties
= properties
34 self
._properties_dict
= support
.TruthDict(properties
)
37 def test(self
, db
, permission
, classname
, property, userid
, itemid
):
38 if permission
!= self
.name
:
41 # are we checking the correct class
42 if self
.klass
is not None and self
.klass
!= classname
:
45 # what about property?
46 if property is not None and not self
._properties_dict
[property]:
50 if itemid
is not None and self
.check
is not None:
51 if not self
.check(db
, userid
, itemid
):
58 return '<Permission 0x%x %r,%r,%r,%r>'%(id(self
), self
.name
,
59 self
.klass
, self
.properties
, self
.check
)
61 def __cmp__(self
, other
):
62 if self
.name
!= other
.name
:
63 return cmp(self
.name
, other
.name
)
65 if self
.klass
!= other
.klass
: return 1
66 if self
.properties
!= other
.properties
: return 1
67 if self
.check
!= other
.check
: return 1
73 ''' Defines a Role with the attributes
78 def __init__(self
, name
='', description
='', permissions
=None):
79 self
.name
= name
.lower()
80 self
.description
= description
81 if permissions
is None:
83 self
.permissions
= permissions
86 return '<Role 0x%x %r,%r>'%(id(self
), self
.name
, self
.permissions
)
89 def __init__(self
, db
):
90 ''' Initialise the permission and role classes, and add in the
91 base roles (for admin user).
93 self
.db
= weakref
.proxy(db
) # use a weak ref to avoid circularity
95 # permssions are mapped by name to a list of Permissions by class
98 # roles are mapped by name to the Role
102 self
.addRole(name
="User", description
="A regular user, no privs")
103 self
.addRole(name
="Admin", description
="An admin user, full privs")
104 self
.addRole(name
="Anonymous", description
="An anonymous user")
106 # default permissions - Admin may do anything
107 for p
in 'create edit retire view'.split():
108 p
= self
.addPermission(name
=p
.title(),
109 description
="User may %s everthing"%p
)
110 self
.addPermissionToRole('Admin', p
)
112 # initialise the permissions and roles needed for the UIs
113 from roundup
.cgi
import client
114 client
.initialiseSecurity(self
)
115 from roundup
import mailgw
116 mailgw
.initialiseSecurity(self
)
118 def getPermission(self
, permission
, classname
=None, properties
=None,
120 ''' Find the Permission matching the name and for the class, if the
121 classname is specified.
123 Raise ValueError if there is no exact match.
125 if not self
.permission
.has_key(permission
):
126 raise ValueError, 'No permission "%s" defined'%permission
130 self
.db
.getclass(classname
)
132 raise ValueError, 'No class "%s" defined'%classname
134 # look through all the permissions of the given name
135 tester
= Permission(permission
, klass
=classname
, properties
=properties
,
137 for perm
in self
.permission
[permission
]:
140 raise ValueError, 'No permission "%s" defined for "%s"'%(permission
,
143 def hasPermission(self
, permission
, userid
, classname
=None,
144 property=None, itemid
=None):
145 '''Look through all the Roles, and hence Permissions, and
146 see if "permission" exists given the constraints of
147 classname, property and itemid.
149 If classname is specified (and only classname) then the
150 search will match if there is *any* Permission for that
151 classname, even if the Permission has additional
154 If property is specified, the Permission matched must have
155 either no properties listed or the property must appear in
158 If itemid is specified, the Permission matched must have
159 either no check function defined or the check function,
160 when invoked, must return a True value.
162 Note that this functionality is actually implemented by the
163 Permission.test() method.
165 if itemid
and classname
is None:
166 raise ValueError, 'classname must accompany itemid'
167 for rolename
in self
.db
.user
.get_roles(userid
):
168 if not rolename
or not self
.role
.has_key(rolename
):
170 # for each of the user's Roles, check the permissions
171 for perm
in self
.role
[rolename
].permissions
:
173 if perm
.test(self
.db
, permission
, classname
, property,
178 def addPermission(self
, **propspec
):
179 ''' Create a new Permission with the properties defined in
180 'propspec'. See the Permission class for the possible
183 perm
= Permission(**propspec
)
184 self
.permission
.setdefault(perm
.name
, []).append(perm
)
187 def addRole(self
, **propspec
):
188 ''' Create a new Role with the properties defined in 'propspec'
190 role
= Role(**propspec
)
191 self
.role
[role
.name
] = role
194 def addPermissionToRole(self
, rolename
, permission
, classname
=None,
195 properties
=None, check
=None):
196 ''' Add the permission to the role's permission list.
198 'rolename' is the name of the role to add the permission to.
200 'permission' is either a Permission *or* a permission name
201 accompanied by 'classname' (thus in the second case a Permission
202 is obtained by passing 'permission' and 'classname' to
205 if not isinstance(permission
, Permission
):
206 permission
= self
.getPermission(permission
, classname
,
208 role
= self
.role
[rolename
.lower()]
209 role
.permissions
.append(permission
)
211 # vim: set filetype=python sts=4 sw=4 et si :