Mercurial > p > roundup > code
comparison roundup/security.py @ 6012:06e6bc21b67e
flake8 changes whitepace and formatting
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Tue, 31 Dec 2019 21:34:24 -0500 |
| parents | 3fa026621f69 |
| children | c12377fb4144 |
comparison
equal
deleted
inserted
replaced
| 6011:8bd93c8e98a6 | 6012:06e6bc21b67e |
|---|---|
| 6 | 6 |
| 7 from roundup import hyperdb, support | 7 from roundup import hyperdb, support |
| 8 | 8 |
| 9 import logging | 9 import logging |
| 10 logger = logging.getLogger('roundup.security') | 10 logger = logging.getLogger('roundup.security') |
| 11 | |
| 11 | 12 |
| 12 class Permission: | 13 class Permission: |
| 13 ''' Defines a Permission with the attributes | 14 ''' Defines a Permission with the attributes |
| 14 - name | 15 - name |
| 15 - description | 16 - description |
| 41 db.security.set_props_only_default() | 42 db.security.set_props_only_default() |
| 42 | 43 |
| 43 with a True or False value. | 44 with a True or False value. |
| 44 ''' | 45 ''' |
| 45 | 46 |
| 46 limit_perm_to_props_only=False | 47 limit_perm_to_props_only = False |
| 47 | 48 |
| 48 def __init__(self, name='', description='', klass=None, | 49 def __init__(self, name='', description='', klass=None, |
| 49 properties=None, check=None, props_only=None): | 50 properties=None, check=None, props_only=None): |
| 50 from roundup.anypy import findargspec | 51 from roundup.anypy import findargspec |
| 51 self.name = name | 52 self.name = name |
| 52 self.description = description | 53 self.description = description |
| 53 self.klass = klass | 54 self.klass = klass |
| 54 self.properties = properties | 55 self.properties = properties |
| 69 # see note on use of bool() in set_props_only_default() | 70 # see note on use of bool() in set_props_only_default() |
| 70 self.limit_perm_to_props_only = bool(props_only) | 71 self.limit_perm_to_props_only = bool(props_only) |
| 71 else: | 72 else: |
| 72 self.limit_perm_to_props_only = None | 73 self.limit_perm_to_props_only = None |
| 73 | 74 |
| 74 | |
| 75 if check is None: | 75 if check is None: |
| 76 self.check_version = 0 | 76 self.check_version = 0 |
| 77 else: | 77 else: |
| 78 args=findargspec.findargspec(check) | 78 args = findargspec.findargspec(check) |
| 79 # args[2] is the keywords argument. Leave it as a subscript and | 79 # args[2] is the keywords argument. Leave it as a subscript and |
| 80 # do not use named tuple reference as names change in python 3. | 80 # do not use named tuple reference as names change in python 3. |
| 81 # If there is a **parameter defined in the function spec, the | 81 # If there is a **parameter defined in the function spec, the |
| 82 # value of the 3rd argument (2nd index) in the tuple is not None. | 82 # value of the 3rd argument (2nd index) in the tuple is not None. |
| 83 if args[2] is None: | 83 if args[2] is None: |
| 119 if itemid is not None and self.check is not None: | 119 if itemid is not None and self.check is not None: |
| 120 if self.check_version == 1: | 120 if self.check_version == 1: |
| 121 if not self.check(db, userid, itemid): | 121 if not self.check(db, userid, itemid): |
| 122 return 0 | 122 return 0 |
| 123 elif self.check_version == 2: | 123 elif self.check_version == 2: |
| 124 if not self.check(db, userid, itemid, property=property, \ | 124 if not self.check(db, userid, itemid, property=property, |
| 125 permission=permission, classname=classname): | 125 permission=permission, classname=classname): |
| 126 return 0 | 126 return 0 |
| 127 | 127 |
| 128 # we have a winner | 128 # we have a winner |
| 129 return 1 | 129 return 1 |
| 130 | 130 |
| 147 if self.check: | 147 if self.check: |
| 148 return 0 | 148 return 0 |
| 149 | 149 |
| 150 return 1 | 150 return 1 |
| 151 | 151 |
| 152 | |
| 153 def __repr__(self): | 152 def __repr__(self): |
| 154 return '<Permission 0x%x %r,%r,%r,%r,%r>'%(id(self), self.name, | 153 return '<Permission 0x%x %r,%r,%r,%r,%r>' % (id(self), self.name, |
| 155 self.klass, self.properties, self.check, | 154 self.klass, self.properties, self.check, |
| 156 self.limit_perm_to_props_only) | 155 self.limit_perm_to_props_only) |
| 157 | 156 |
| 158 def __eq__(self, other): | 157 def __eq__(self, other): |
| 159 if self.name != other.name: | 158 if self.name != other.name: |
| 161 | 160 |
| 162 if self.klass != other.klass: return False | 161 if self.klass != other.klass: return False |
| 163 if self.properties != other.properties: return False | 162 if self.properties != other.properties: return False |
| 164 if self.check != other.check: return False | 163 if self.check != other.check: return False |
| 165 if self.limit_perm_to_props_only != \ | 164 if self.limit_perm_to_props_only != \ |
| 166 other.limit_perm_to_props_only: return False | 165 other.limit_perm_to_props_only: return False |
| 167 | 166 |
| 168 # match | 167 # match |
| 169 return True | 168 return True |
| 170 | 169 |
| 171 def __ne__(self, other): | 170 def __ne__(self, other): |
| 172 return not self.__eq__(other) | 171 return not self.__eq__(other) |
| 173 | 172 |
| 174 def __getitem__(self,index): | 173 def __getitem__(self, index): |
| 175 return (self.name, self.klass, self.properties, self.check, | 174 return (self.name, self.klass, self.properties, self.check, |
| 176 self.limit_perm_to_props_only)[index] | 175 self.limit_perm_to_props_only)[index] |
| 176 | |
| 177 | 177 |
| 178 class Role: | 178 class Role: |
| 179 ''' Defines a Role with the attributes | 179 ''' Defines a Role with the attributes |
| 180 - name | 180 - name |
| 181 - description | 181 - description |
| 187 if permissions is None: | 187 if permissions is None: |
| 188 permissions = [] | 188 permissions = [] |
| 189 self.permissions = permissions | 189 self.permissions = permissions |
| 190 | 190 |
| 191 def __repr__(self): | 191 def __repr__(self): |
| 192 return '<Role 0x%x %r,%r>'%(id(self), self.name, self.permissions) | 192 return '<Role 0x%x %r,%r>' % (id(self), self.name, self.permissions) |
| 193 | |
| 193 | 194 |
| 194 class Security: | 195 class Security: |
| 195 def __init__(self, db): | 196 def __init__(self, db): |
| 196 ''' Initialise the permission and role classes, and add in the | 197 ''' Initialise the permission and role classes, and add in the |
| 197 base roles (for admin user). | 198 base roles (for admin user). |
| 210 self.addRole(name="Anonymous", description="An anonymous user") | 211 self.addRole(name="Anonymous", description="An anonymous user") |
| 211 | 212 |
| 212 # default permissions - Admin may do anything | 213 # default permissions - Admin may do anything |
| 213 for p in 'create edit restore retire view'.split(): | 214 for p in 'create edit restore retire view'.split(): |
| 214 p = self.addPermission(name=p.title(), | 215 p = self.addPermission(name=p.title(), |
| 215 description="User may %s everything"%p) | 216 description="User may %s everything" % p) |
| 216 self.addPermissionToRole('Admin', p) | 217 self.addPermissionToRole('Admin', p) |
| 217 | 218 |
| 218 # initialise the permissions and roles needed for the UIs | 219 # initialise the permissions and roles needed for the UIs |
| 219 from roundup.cgi import client | 220 from roundup.cgi import client |
| 220 client.initialiseSecurity(self) | 221 client.initialiseSecurity(self) |
| 221 from roundup import mailgw | 222 from roundup import mailgw |
| 222 mailgw.initialiseSecurity(self) | 223 mailgw.initialiseSecurity(self) |
| 223 | 224 |
| 224 def getPermission(self, permission, classname=None, properties=None, | 225 def getPermission(self, permission, classname=None, properties=None, |
| 225 check=None, props_only=None): | 226 check=None, props_only=None): |
| 226 ''' Find the Permission matching the name and for the class, if the | 227 ''' Find the Permission matching the name and for the class, if the |
| 227 classname is specified. | 228 classname is specified. |
| 228 | 229 |
| 229 Raise ValueError if there is no exact match. | 230 Raise ValueError if there is no exact match. |
| 230 ''' | 231 ''' |
| 231 if permission not in self.permission: | 232 if permission not in self.permission: |
| 232 raise ValueError('No permission "%s" defined'%permission) | 233 raise ValueError('No permission "%s" defined' % permission) |
| 233 | 234 |
| 234 if classname: | 235 if classname: |
| 235 try: | 236 try: |
| 236 self.db.getclass(classname) | 237 self.db.getclass(classname) |
| 237 except KeyError: | 238 except KeyError: |
| 238 raise ValueError('No class "%s" defined'%classname) | 239 raise ValueError('No class "%s" defined' % classname) |
| 239 | 240 |
| 240 # look through all the permissions of the given name | 241 # look through all the permissions of the given name |
| 241 tester = Permission(permission, klass=classname, properties=properties, | 242 tester = Permission(permission, klass=classname, properties=properties, |
| 242 check=check, | 243 check=check, |
| 243 props_only=props_only) | 244 props_only=props_only) |
| 244 for perm in self.permission[permission]: | 245 for perm in self.permission[permission]: |
| 245 if perm == tester: | 246 if perm == tester: |
| 246 return perm | 247 return perm |
| 247 raise ValueError('No permission "%s" defined for "%s"'%(permission, | 248 raise ValueError('No permission "%s" defined for "%s"' % (permission, |
| 248 classname)) | 249 classname)) |
| 249 | 250 |
| 250 def hasPermission(self, permission, userid, classname=None, | 251 def hasPermission(self, permission, userid, classname=None, |
| 251 property=None, itemid=None): | 252 property=None, itemid=None): |
| 252 '''Look through all the Roles, and hence Permissions, and | 253 '''Look through all the Roles, and hence Permissions, and |
| 253 see if "permission" exists given the constraints of | 254 see if "permission" exists given the constraints of |
| 254 classname, property, itemid, and props_only. | 255 classname, property, itemid, and props_only. |
| 255 | 256 |
| 256 If classname is specified (and only classname) the | 257 If classname is specified (and only classname) the |
| 284 continue | 285 continue |
| 285 # for each of the user's Roles, check the permissions | 286 # for each of the user's Roles, check the permissions |
| 286 for perm in self.role[rolename].permissions: | 287 for perm in self.role[rolename].permissions: |
| 287 # permission match? | 288 # permission match? |
| 288 if perm.test(self.db, permission, classname, property, | 289 if perm.test(self.db, permission, classname, property, |
| 289 userid, itemid): | 290 userid, itemid): |
| 290 return 1 | 291 return 1 |
| 291 return 0 | 292 return 0 |
| 292 | 293 |
| 293 def roleHasSearchPermission(self, classname, property, *rolenames): | 294 def roleHasSearchPermission(self, classname, property, *rolenames): |
| 294 """ For each of the given roles, check the permissions. | 295 """ For each of the given roles, check the permissions. |
| 295 Property can be a transitive property. | 296 Property can be a transitive property. |
| 296 """ | 297 """ |
| 297 perms = [] | 298 perms = [] |
| 298 # pre-compute permissions | 299 # pre-compute permissions |
| 299 for rn in rolenames : | 300 for rn in rolenames: |
| 300 for perm in self.role[rn].permissions: | 301 for perm in self.role[rn].permissions: |
| 301 perms.append(perm) | 302 perms.append(perm) |
| 302 # Note: break from inner loop means "found" | 303 # Note: break from inner loop means "found" |
| 303 # break from outer loop means "not found" | 304 # break from outer loop means "not found" |
| 304 cn = classname | 305 cn = classname |
| 361 either no properties listed or the property must appear in | 362 either no properties listed or the property must appear in |
| 362 the list. | 363 the list. |
| 363 ''' | 364 ''' |
| 364 roles = [r for r in self.db.user.get_roles(userid) | 365 roles = [r for r in self.db.user.get_roles(userid) |
| 365 if r and (r in self.role)] | 366 if r and (r in self.role)] |
| 366 return self.roleHasSearchPermission (classname, property, *roles) | 367 return self.roleHasSearchPermission(classname, property, *roles) |
| 367 | 368 |
| 368 def addPermission(self, **propspec): | 369 def addPermission(self, **propspec): |
| 369 ''' Create a new Permission with the properties defined in | 370 ''' Create a new Permission with the properties defined in |
| 370 'propspec'. See the Permission class for the possible | 371 'propspec'. See the Permission class for the possible |
| 371 keyword args. | 372 keyword args. |
| 392 | 393 |
| 393 def get_props_only_default(self): | 394 def get_props_only_default(self): |
| 394 return Permission.limit_perm_to_props_only | 395 return Permission.limit_perm_to_props_only |
| 395 | 396 |
| 396 def addPermissionToRole(self, rolename, permission, classname=None, | 397 def addPermissionToRole(self, rolename, permission, classname=None, |
| 397 properties=None, check=None, props_only=None): | 398 properties=None, check=None, props_only=None): |
| 398 ''' Add the permission to the role's permission list. | 399 ''' Add the permission to the role's permission list. |
| 399 | 400 |
| 400 'rolename' is the name of the role to add the permission to. | 401 'rolename' is the name of the role to add the permission to. |
| 401 | 402 |
| 402 'permission' is either a Permission *or* a permission name | 403 'permission' is either a Permission *or* a permission name |
| 404 is obtained by passing 'permission' and 'classname' to | 405 is obtained by passing 'permission' and 'classname' to |
| 405 self.getPermission) | 406 self.getPermission) |
| 406 ''' | 407 ''' |
| 407 if not isinstance(permission, Permission): | 408 if not isinstance(permission, Permission): |
| 408 permission = self.getPermission(permission, classname, | 409 permission = self.getPermission(permission, classname, |
| 409 properties, check, props_only) | 410 properties, check, props_only) |
| 410 role = self.role[rolename.lower()] | 411 role = self.role[rolename.lower()] |
| 411 role.permissions.append(permission) | 412 role.permissions.append(permission) |
| 412 | 413 |
| 413 # Convenience methods for removing non-allowed properties from a | 414 # Convenience methods for removing non-allowed properties from a |
| 414 # filterspec or sort/group list | 415 # filterspec or sort/group list |
| 415 | 416 |
| 416 def filterFilterspec(self, userid, classname, filterspec): | 417 def filterFilterspec(self, userid, classname, filterspec): |
| 417 """ Return a filterspec that has all non-allowed properties removed. | 418 """ Return a filterspec that has all non-allowed properties removed. |
| 418 """ | 419 """ |
| 419 return dict ([(k, v) for k, v in filterspec.items() | 420 return dict([(k, v) for k, v in filterspec.items() |
| 420 if self.hasSearchPermission(userid,classname,k)]) | 421 if self.hasSearchPermission(userid, classname, k)]) |
| 421 | 422 |
| 422 def filterSortspec(self, userid, classname, sort): | 423 def filterSortspec(self, userid, classname, sort): |
| 423 """ Return a sort- or group-list that has all non-allowed properties | 424 """ Return a sort- or group-list that has all non-allowed properties |
| 424 removed. | 425 removed. |
| 425 """ | 426 """ |
| 426 if isinstance(sort, tuple) and sort[0] in '+-': | 427 if isinstance(sort, tuple) and sort[0] in '+-': |
| 427 sort = [sort] | 428 sort = [sort] |
| 428 return [(d, p) for d, p in sort | 429 return [(d, p) for d, p in sort |
| 429 if self.hasSearchPermission(userid,classname,p)] | 430 if self.hasSearchPermission(userid, classname, p)] |
| 430 | 431 |
| 431 # vim: set filetype=python sts=4 sw=4 et si : | 432 # vim: set filetype=python sts=4 sw=4 et si : |
