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