## $Id: dynamicroles.py 7256 2011-12-03 05:46:52Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Security policy components for students. Students need special security policy treatment, as officers with local roles for departments and faculties might have additional permissions (local roles on depts/faculties) here. """ import grok from zope.securitypolicy.interfaces import IPrincipalRoleManager from zope.securitypolicy.principalpermission import ( AnnotationPrincipalPermissionManager,) from zope.securitypolicy.principalrole import AnnotationPrincipalRoleManager from waeup.sirp.students.interfaces import IStudent # All components in here have the same context: Student instances grok.context(IStudent) class StudentPrincipalRoleManager(AnnotationPrincipalRoleManager, grok.Adapter): grok.provides(IPrincipalRoleManager) #: The attribute name to lookup for additional roles extra_attrib = 'certificate' subcontainer = 'studycourse' # Role name mapping: # role name to look for in `extra_attrib` and parents # to # role to add in case this role was found rolename_mapping = { 'waeup.local.ClearanceOfficer':'waeup.StudentsClearanceOfficer', 'waeup.local.CourseAdviser100':'waeup.StudentsCourseAdviser100', 'waeup.local.CourseAdviser200':'waeup.StudentsCourseAdviser200', 'waeup.local.CourseAdviser300':'waeup.StudentsCourseAdviser300', 'waeup.local.CourseAdviser400':'waeup.StudentsCourseAdviser400', 'waeup.local.CourseAdviser500':'waeup.StudentsCourseAdviser500', 'waeup.local.CourseAdviser600':'waeup.StudentsCourseAdviser600' } def getRolesForPrincipal(self, principal_id): """Get roles for principal with id `principal_id`. Different to the default implementation, this method also takes into account local roles set on any department connected to the context student. If the given principal has at least one of the `external_rolenames` roles granted for the external object, it additionally gets `additional_rolename` role for the context student. For the additional roles the `extra_attrib` and all its parent objects are looked up, because 'role inheritance' does not work on that basic level of permission handling. Some advantages of this approach: - we don't have to store extra local roles for clearance officers in ZODB for each student - when local roles on a department change, we don't have to update thousands of students; the local role is assigned dynamically. Disadvantage: - More expensive role lookups when a clearance officer wants to see an student form. This implementation is designed to be usable also for other contexts than students. You can inherit from it and set different role names to lookup/set easily via the static class attributes. """ apr_manager = AnnotationPrincipalRoleManager(self._context) result = apr_manager.getRolesForPrincipal(principal_id) if result != []: # If there are local roles defined here, no additional # lookup is done. return result # The principal has no local roles yet. Let's lookup the # connected course, dept, etc. if self.subcontainer: obj = getattr( self._context[self.subcontainer], self.extra_attrib, None) else: obj = getattr(self._context, self.extra_attrib, None) # lookup local roles for connected course and all parent # objects. This way we fake 'role inheritance'. while obj is not None: extra_roles = IPrincipalRoleManager(obj).getRolesForPrincipal( principal_id) for role_id, setting in extra_roles: if role_id in self.rolename_mapping.keys(): # Found role in external attribute or parent # thereof. 'Grant' additional role # permissions (allow, deny or unset) for the # passed in principal id. result.append( (self.rolename_mapping[role_id], setting)) return result obj = getattr(obj, '__parent__', None) return result