source: main/waeup.sirp/trunk/src/waeup/sirp/students/dynamicroles.py @ 7335

Last change on this file since 7335 was 7334, checked in by Henrik Bettermann, 13 years ago

Implement local CourseAdviser? roles. These roles can be assigned in departments and certificates. There are 6 different roles, one for each study level. getRolesForPrincipal grants the additional waeup.StudentsCourseAdviser? role only if the current level of a student corresponds with the level number in the external role name.

To do: Assign local roles on CertificateManageFormPage?. Add browser tests.

  • Property svn:keywords set to Id
File size: 5.5 KB
Line 
1## $Id: dynamicroles.py 7334 2011-12-12 14:11:21Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Security policy components for students.
19
20Students need special security policy treatment, as officers with
21local roles for departments and faculties might have additional
22permissions (local roles on depts/faculties) here.
23"""
24import grok
25from zope.securitypolicy.interfaces import IPrincipalRoleManager
26from zope.securitypolicy.principalpermission import (
27    AnnotationPrincipalPermissionManager,)
28from zope.securitypolicy.principalrole import AnnotationPrincipalRoleManager
29from waeup.sirp.students.interfaces import IStudent
30
31# All components in here have the same context: Student instances
32grok.context(IStudent)
33
34class StudentPrincipalRoleManager(AnnotationPrincipalRoleManager,
35                                    grok.Adapter):
36    grok.provides(IPrincipalRoleManager)
37
38    #: The attribute name to lookup for additional roles
39    extra_attrib = 'certificate'
40    subcontainer = 'studycourse'
41
42    # Role name mapping:
43    # role name to look for in `extra_attrib` and parents
44    # to
45    # role to add in case this role was found
46    rolename_mapping = {
47        'waeup.local.ClearanceOfficer':'waeup.StudentsClearanceOfficer',
48        'waeup.local.CourseAdviser100':'waeup.StudentsCourseAdviser',
49        'waeup.local.CourseAdviser200':'waeup.StudentsCourseAdviser',
50        'waeup.local.CourseAdviser300':'waeup.StudentsCourseAdviser',
51        'waeup.local.CourseAdviser400':'waeup.StudentsCourseAdviser',
52        'waeup.local.CourseAdviser500':'waeup.StudentsCourseAdviser',
53        'waeup.local.CourseAdviser600':'waeup.StudentsCourseAdviser'
54        }
55
56    def getRolesForPrincipal(self, principal_id):
57        """Get roles for principal with id `principal_id`.
58
59        Different to the default implementation, this method also
60        takes into account local roles set on any department connected
61        to the context student.
62
63        If the given principal has at least one of the
64        `external_rolenames` roles granted for the external object, it
65        additionally gets `additional_rolename` role for the context
66        student.
67
68        For the additional roles the `extra_attrib` and all its parent
69        objects are looked up, because 'role inheritance' does not
70        work on that basic level of permission handling.
71
72        Some advantages of this approach:
73
74        - we don't have to store extra local roles for clearance
75          officers in ZODB for each student
76
77        - when local roles on a department change, we don't have to
78          update thousands of students; the local role is assigned
79          dynamically.
80
81        Disadvantage:
82
83        - More expensive role lookups when a clearance officer wants
84          to see an student form.
85
86        This implementation is designed to be usable also for other
87        contexts than students. You can inherit from it and set
88        different role names to lookup/set easily via the static class
89        attributes.
90        """
91        apr_manager = AnnotationPrincipalRoleManager(self._context)
92        result = apr_manager.getRolesForPrincipal(principal_id)
93        if result != []:
94            # If there are local roles defined here, no additional
95            # lookup is done.
96            return result
97        # The principal has no local roles yet. Let's lookup the
98        # connected course, dept, etc.
99        if self.subcontainer:
100            obj = getattr(
101                self._context[self.subcontainer], self.extra_attrib, None)
102            current_level = getattr(
103                self._context[self.subcontainer], 'current_level', None)
104        else:
105            obj = getattr(self._context, self.extra_attrib, None)
106            current_level = None
107        # lookup local roles for connected course and all parent
108        # objects. This way we fake 'role inheritance'.
109        while obj is not None:
110            extra_roles = IPrincipalRoleManager(obj).getRolesForPrincipal(
111                principal_id)
112            for role_id, setting in extra_roles:
113                if role_id in self.rolename_mapping.keys():
114                    # Found role in external attribute or parent
115                    # thereof. 'Grant' additional role
116                    # permissions (allow, deny or unset) for the
117                    # passed in principal id.
118                    # Make an exception for Course Advisers:
119                    # Grant additional role only if external role corresponds
120                    # with current_level of student.
121                    if not current_level or \
122                        'CourseAdviser' not in  role_id or \
123                        str(current_level) in role_id:
124                        result.append(
125                            (self.rolename_mapping[role_id], setting))
126                    return result
127            obj = getattr(obj, '__parent__', None)
128        return result
Note: See TracBrowser for help on using the repository browser.