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 import cgi_client, mailgw
70 cgi_client.initialiseSecurity(self)
71 mailgw.initialiseSecurity(self)
73 def getPermission(self, permission, classname=None):
74 ''' Find the Permission matching the name and for the class, if the
75 classname is specified.
77 Raise ValueError if there is no exact match.
78 '''
79 if not self.permission.has_key(permission):
80 raise ValueError, 'No permission "%s" defined'%permission
82 # look through all the permissions of the given name
83 for perm in self.permission[permission]:
84 # if we're passed a classname, the permission must match
85 if perm.klass is not None and perm.klass == classname:
86 return perm
87 # otherwise the permission klass must be unset
88 elif not perm.klass and not classname:
89 return perm
90 raise ValueError, 'No permission "%s" defined for "%s"'%(permission,
91 classname)
93 def hasPermission(self, permission, userid, classname=None):
94 ''' Look through all the Roles, and hence Permissions, and see if
95 "permission" is there for the specified classname.
96 '''
97 roles = self.db.user.get(userid, 'roles')
98 if roles is None:
99 return 0
100 for rolename in roles.split(','):
101 if not rolename:
102 continue
103 # for each of the user's Roles, check the permissions
104 for perm in self.role[rolename].permissions:
105 # permission name match?
106 if perm.name == permission:
107 # permission klass match?
108 if perm.klass is None or perm.klass == classname:
109 # we have a winner
110 return 1
111 return 0
113 def hasNodePermission(self, classname, nodeid, **propspec):
114 ''' Check the named properties of the given node to see if the
115 userid appears in them. If it does, then the user is granted
116 this permission check.
118 'propspec' consists of a set of properties and values that
119 must be present on the given node for access to be granted.
121 If a property is a Link, the value must match the property
122 value. If a property is a Multilink, the value must appear
123 in the Multilink list.
124 '''
125 klass = self.db.getclass(classname)
126 properties = klass.getprops()
127 for k,v in propspec.items():
128 value = klass.get(nodeid, k)
129 if isinstance(properties[k], hyperdb.Multilink):
130 if v not in value:
131 return 0
132 else:
133 if v != value:
134 return 0
135 return 1
137 def addPermission(self, **propspec):
138 ''' Create a new Permission with the properties defined in
139 'propspec'
140 '''
141 perm = Permission(**propspec)
142 self.permission.setdefault(perm.name, []).append(perm)
143 return perm
145 def addRole(self, **propspec):
146 ''' Create a new Role with the properties defined in 'propspec'
147 '''
148 role = Role(**propspec)
149 self.role[role.name] = role
150 return role
152 def addPermissionToRole(self, rolename, permission):
153 ''' Add the permission to the role's permission list.
155 'rolename' is the name of the role to add the permission to.
156 '''
157 role = self.role[rolename]
158 role.permissions.append(permission)