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

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

More copyright adjustments.

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