1 """Handle the security declarations used in Roundup trackers.
2 """
3 __docformat__ = 'restructuredtext'
5 import weakref
7 from roundup import hyperdb
9 class Permission:
10 ''' Defines a Permission with the attributes
11 - name
12 - description
13 - klass (optional)
15 The klass may be unset, indicating that this permission is not
16 locked to a particular class. That means there may be multiple
17 Permissions for the same name for different classes.
18 '''
19 def __init__(self, name='', description='', klass=None):
20 self.name = name
21 self.description = description
22 self.klass = klass
24 def __repr__(self):
25 return '<Permission 0x%x %r,%r>'%(id(self), self.name, self.klass)
27 class Role:
28 ''' Defines a Role with the attributes
29 - name
30 - description
31 - permissions
32 '''
33 def __init__(self, name='', description='', permissions=None):
34 self.name = name.lower()
35 self.description = description
36 if permissions is None:
37 permissions = []
38 self.permissions = permissions
40 def __repr__(self):
41 return '<Role 0x%x %r,%r>'%(id(self), self.name, self.permissions)
43 class Security:
44 def __init__(self, db):
45 ''' Initialise the permission and role classes, and add in the
46 base roles (for admin user).
47 '''
48 self.db = weakref.proxy(db) # use a weak ref to avoid circularity
50 # permssions are mapped by name to a list of Permissions by class
51 self.permission = {}
53 # roles are mapped by name to the Role
54 self.role = {}
56 # the default Roles
57 self.addRole(name="User", description="A regular user, no privs")
58 self.addRole(name="Admin", description="An admin user, full privs")
59 self.addRole(name="Anonymous", description="An anonymous user")
61 ee = self.addPermission(name="Edit",
62 description="User may edit everthing")
63 self.addPermissionToRole('Admin', ee)
64 ae = self.addPermission(name="View",
65 description="User may access everything")
66 self.addPermissionToRole('Admin', ae)
67 reg = self.addPermission(name="Register Web",
68 description="User may register through the web")
69 reg = self.addPermission(name="Register Email",
70 description="User may register through the email")
72 # initialise the permissions and roles needed for the UIs
73 from roundup.cgi import client
74 client.initialiseSecurity(self)
75 from roundup import mailgw
76 mailgw.initialiseSecurity(self)
78 def getPermission(self, permission, classname=None):
79 ''' Find the Permission matching the name and for the class, if the
80 classname is specified.
82 Raise ValueError if there is no exact match.
83 '''
84 if not self.permission.has_key(permission):
85 raise ValueError, 'No permission "%s" defined'%permission
87 # look through all the permissions of the given name
88 for perm in self.permission[permission]:
89 # if we're passed a classname, the permission must match
90 if perm.klass is not None and perm.klass == classname:
91 return perm
92 # otherwise the permission klass must be unset
93 elif not perm.klass and not classname:
94 return perm
95 raise ValueError, 'No permission "%s" defined for "%s"'%(permission,
96 classname)
98 def hasPermission(self, permission, userid, classname=None):
99 ''' Look through all the Roles, and hence Permissions, and see if
100 "permission" is there for the specified classname.
101 '''
102 roles = self.db.user.get(userid, 'roles')
103 if roles is None:
104 return 0
105 for rolename in [x.lower().strip() for x in roles.split(',')]:
106 if not rolename or not self.role.has_key(rolename):
107 continue
108 # for each of the user's Roles, check the permissions
109 for perm in self.role[rolename].permissions:
110 # permission name match?
111 if perm.name == permission:
112 # permission klass match?
113 if perm.klass is None or perm.klass == classname:
114 # we have a winner
115 return 1
116 return 0
118 def hasNodePermission(self, classname, nodeid, **propspec):
119 ''' Check the named properties of the given node to see if the
120 userid appears in them. If it does, then the user is granted
121 this permission check.
123 'propspec' consists of a set of properties and values that
124 must be present on the given node for access to be granted.
126 If a property is a Link, the value must match the property
127 value. If a property is a Multilink, the value must appear
128 in the Multilink list.
129 '''
130 klass = self.db.getclass(classname)
131 properties = klass.getprops()
132 for k,v in propspec.items():
133 value = klass.get(nodeid, k)
134 if isinstance(properties[k], hyperdb.Multilink):
135 if v not in value:
136 return 0
137 else:
138 if v != value:
139 return 0
140 return 1
142 def addPermission(self, **propspec):
143 ''' Create a new Permission with the properties defined in
144 'propspec'
145 '''
146 perm = Permission(**propspec)
147 self.permission.setdefault(perm.name, []).append(perm)
148 return perm
150 def addRole(self, **propspec):
151 ''' Create a new Role with the properties defined in 'propspec'
152 '''
153 role = Role(**propspec)
154 self.role[role.name] = role
155 return role
157 def addPermissionToRole(self, rolename, permission):
158 ''' Add the permission to the role's permission list.
160 'rolename' is the name of the role to add the permission to.
161 '''
162 role = self.role[rolename.lower()]
163 role.permissions.append(permission)
165 # vim: set filetype=python ts=4 sw=4 et si