source: main/waeup.sirp/trunk/src/waeup/sirp/authentication.py @ 7185

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

Rename roles:

Department Officers are now Department Managers and Portal Users are now called Academics Officers. Academics Officers only have the viewAcademics permission like applicants and students.

  • Property svn:keywords set to Id
File size: 8.8 KB
Line 
1"""Authentication for WAeUP portals.
2"""
3import grok
4from zope.event import notify
5from zope.component import getUtility, getUtilitiesFor
6from zope.interface import Interface
7from zope.securitypolicy.interfaces import (
8    IPrincipalRoleMap, IPrincipalRoleManager)
9from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
10from zope.pluggableauth.interfaces import (
11        ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo)
12from zope.password.interfaces import IPasswordManager
13from zope.securitypolicy.principalrole import principalRoleManager
14from waeup.sirp.interfaces import (ILocalRoleSetEvent,
15    IUserAccount, IAuthPluginUtility, IPasswordValidator)
16
17def setup_authentication(pau):
18    """Set up plugguble authentication utility.
19
20    Sets up an IAuthenticatorPlugin and
21    ICredentialsPlugin (for the authentication mechanism)
22
23    Then looks for any external utilities that want to modify the PAU.
24    """
25    pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials')
26    pau.authenticatorPlugins = ('users',)
27
28    # Give any third-party code and subpackages a chance to modify the PAU
29    auth_plugin_utilities = getUtilitiesFor(IAuthPluginUtility)
30    for name, util in auth_plugin_utilities:
31        util.register(pau)
32
33def get_principal_role_manager():
34    """Get a role manager for principals.
35
36    If we are currently 'in a site', return the role manager for the
37    portal or the global rolemanager else.
38    """
39    portal = grok.getSite()
40    if portal is not None:
41        return IPrincipalRoleManager(portal)
42    return principalRoleManager
43
44class WAeUPSessionCredentialsPlugin(grok.GlobalUtility,
45                                    SessionCredentialsPlugin):
46    grok.provides(ICredentialsPlugin)
47    grok.name('credentials')
48
49    loginpagename = 'login'
50    loginfield = 'form.login'
51    passwordfield = 'form.password'
52
53class PrincipalInfo(object):
54    grok.implements(IPrincipalInfo)
55
56    def __init__(self, id, title, description):
57        self.id = id
58        self.title = title
59        self.description = description
60        self.credentialsPlugin = None
61        self.authenticatorPlugin = None
62
63class Account(grok.Model):
64    grok.implements(IUserAccount)
65
66    _local_roles = dict()
67
68    def __init__(self, name, password, title=None, description=None,
69                 roles = []):
70        self.name = name
71        if title is None:
72            title = name
73        if description is None:
74            description = title
75        self.title = title
76        self.description = description
77        self.setPassword(password)
78        #self.setSiteRolesForPrincipal(roles)
79        # We don't want to share this dict with other accounts
80        self._local_roles = dict()
81
82    def setPassword(self, password):
83        passwordmanager = getUtility(IPasswordManager, 'SHA1')
84        self.password = passwordmanager.encodePassword(password)
85
86    def checkPassword(self, password):
87        passwordmanager = getUtility(IPasswordManager, 'SHA1')
88        return passwordmanager.checkPassword(self.password, password)
89
90    def getSiteRolesForPrincipal(self):
91        prm = get_principal_role_manager()
92        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
93                 if x[0].startswith('waeup.')]
94        return roles
95
96    def setSiteRolesForPrincipal(self, roles):
97        prm = get_principal_role_manager()
98        old_roles = self.getSiteRolesForPrincipal()
99        for role in old_roles:
100            # Remove old roles, not to be set now...
101            if role.startswith('waeup.') and role not in roles:
102                prm.unsetRoleForPrincipal(role, self.name)
103        for role in roles:
104            prm.assignRoleToPrincipal(role, self.name)
105
106    roles = property(getSiteRolesForPrincipal, setSiteRolesForPrincipal)
107
108    def getLocalRoles(self):
109        return self._local_roles
110
111    def notifyLocalRoleChanged(self, obj, role_id, granted=True):
112        objects = self._local_roles.get(role_id, [])
113        if granted and obj not in objects:
114            objects.append(obj)
115        if not granted and obj in objects:
116            objects.remove(obj)
117        self._local_roles[role_id] = objects
118        if len(objects) == 0:
119            del self._local_roles[role_id]
120        self._p_changed = True
121        return
122
123class UserAuthenticatorPlugin(grok.GlobalUtility):
124    grok.implements(IAuthenticatorPlugin)
125    grok.provides(IAuthenticatorPlugin)
126    grok.name('users')
127
128    def authenticateCredentials(self, credentials):
129        if not isinstance(credentials, dict):
130            return None
131        if not ('login' in credentials and 'password' in credentials):
132            return None
133        account = self.getAccount(credentials['login'])
134
135        if account is None:
136            return None
137        if not account.checkPassword(credentials['password']):
138            return None
139        return PrincipalInfo(id=account.name,
140                             title=account.title,
141                             description=account.description)
142
143    def principalInfo(self, id):
144        account = self.getAccount(id)
145        if account is None:
146            return None
147        return PrincipalInfo(id=account.name,
148                             title=account.title,
149                             description=account.description)
150
151    def getAccount(self, login):
152        # ... look up the account object and return it ...
153        userscontainer = self.getUsersContainer()
154        if userscontainer is None:
155            return
156        return userscontainer.get(login, None)
157
158    def addAccount(self, account):
159        userscontainer = self.getUsersContainer()
160        if userscontainer is None:
161            return
162        # XXX: complain if name already exists...
163        userscontainer.addAccount(account)
164
165    def addUser(self, name, password, title=None, description=None):
166        userscontainer = self.getUsersContainer()
167        if userscontainer is None:
168            return
169        userscontainer.addUser(name, password, title, description)
170
171    def getUsersContainer(self):
172        site = grok.getSite()
173        return site['users']
174
175class PasswordValidator(grok.GlobalUtility):
176
177  grok.implements(IPasswordValidator)
178
179  def validate_password(self, pw, pw_repeat):
180       errors = []
181       if len(pw) < 3:
182         errors.append('Password must have at least 3 chars.')
183       if pw != pw_repeat:
184         errors.append('Passwords do not match.')
185       return errors
186
187class LocalRoleSetEvent(object):
188
189    grok.implements(ILocalRoleSetEvent)
190
191    def __init__(self, object, role_id, principal_id, granted=True):
192        self.object = object
193        self.role_id = role_id
194        self.principal_id = principal_id
195        self.granted = granted
196
197@grok.subscribe(IUserAccount, grok.IObjectRemovedEvent)
198def handle_account_removed(account, event):
199    """When an account is removed, local roles might have to be deleted.
200    """
201    local_roles = account.getLocalRoles()
202    principal = account.name
203    for role_id, object_list in local_roles.items():
204        for object in object_list:
205            try:
206                role_manager = IPrincipalRoleManager(object)
207            except TypeError:
208                # No role manager, no roles to remove
209                continue
210            role_manager.unsetRoleForPrincipal(role_id, principal)
211    return
212
213@grok.subscribe(IUserAccount, grok.IObjectAddedEvent)
214def handle_account_added(account, event):
215    """When an account is added, the local owner role and the global
216    AcademicsOfficer role must be set.
217    """
218    # We set the local Owner role
219    role_manager_account = IPrincipalRoleManager(account)
220    role_manager_account.assignRoleToPrincipal(
221        'waeup.local.Owner', account.name)
222    # We set the global AcademicsOfficer role
223    site = grok.getSite()
224    role_manager_site = IPrincipalRoleManager(site)
225    role_manager_site.assignRoleToPrincipal(
226        'waeup.AcademicsOfficer', account.name)
227    # Finally we have to notify the user account that the local role
228    # of the same object has changed
229    notify(LocalRoleSetEvent(
230        account, 'waeup.local.Owner', account.name, granted=True))
231    return
232
233@grok.subscribe(Interface, ILocalRoleSetEvent)
234def handle_local_role_changed(obj, event):
235    site = grok.getSite()
236    if site is None:
237        return
238    users = site.get('users', None)
239    if users is None:
240        return
241    role_id = event.role_id
242    if event.principal_id not in users.keys():
243        return
244    user = users[event.principal_id]
245    user.notifyLocalRoleChanged(event.object, event.role_id, event.granted)
246    return
247
248@grok.subscribe(Interface, grok.IObjectRemovedEvent)
249def handle_local_roles_on_obj_removed(obj, event):
250    try:
251        role_map = IPrincipalRoleMap(obj)
252    except TypeError:
253        # no map, no roles to remove
254        return
255    for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
256        notify(LocalRoleSetEvent(
257                obj, local_role, user_name, granted=False))
258    return
Note: See TracBrowser for help on using the repository browser.