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

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

Use prefix for SIRPPrincipal initialization.

I don't know exactly what the authentication prefix is, but it might be important?!

  • Property svn:keywords set to Id
File size: 11.8 KB
Line 
1## $Id: authentication.py 7239 2011-11-29 07:09:05Z 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.factories import Principal
27from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
28from zope.pluggableauth.interfaces import (
29        ICredentialsPlugin, IAuthenticatorPlugin,
30        IAuthenticatedPrincipalFactory, AuthenticatedPrincipalCreated)
31from zope.publisher.interfaces import IRequest
32from zope.password.interfaces import IPasswordManager
33from zope.securitypolicy.principalrole import principalRoleManager
34from waeup.sirp.interfaces import (ILocalRoleSetEvent,
35    IUserAccount, IAuthPluginUtility, IPasswordValidator,
36    ISIRPPrincipal, ISIRPPrincipalInfo)
37
38def setup_authentication(pau):
39    """Set up plugguble authentication utility.
40
41    Sets up an IAuthenticatorPlugin and
42    ICredentialsPlugin (for the authentication mechanism)
43
44    Then looks for any external utilities that want to modify the PAU.
45    """
46    pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials')
47    pau.authenticatorPlugins = ('users',)
48
49    # Give any third-party code and subpackages a chance to modify the PAU
50    auth_plugin_utilities = getUtilitiesFor(IAuthPluginUtility)
51    for name, util in auth_plugin_utilities:
52        util.register(pau)
53
54def get_principal_role_manager():
55    """Get a role manager for principals.
56
57    If we are currently 'in a site', return the role manager for the
58    portal or the global rolemanager else.
59    """
60    portal = grok.getSite()
61    if portal is not None:
62        return IPrincipalRoleManager(portal)
63    return principalRoleManager
64
65class WAeUPSessionCredentialsPlugin(grok.GlobalUtility,
66                                    SessionCredentialsPlugin):
67    grok.provides(ICredentialsPlugin)
68    grok.name('credentials')
69
70    loginpagename = 'login'
71    loginfield = 'form.login'
72    passwordfield = 'form.password'
73
74class SIRPPrincipalInfo(object):
75    """An implementation of ISIRPPrincipalInfo.
76
77    A SIRP principal info is created with id, login, title, description,
78    phone and email.
79    """
80    grok.implements(ISIRPPrincipalInfo)
81
82    def __init__(self, id, title, description, email, phone):
83        self.id = id
84        self.title = title
85        self.description = description
86        self.email = email
87        self.phone = phone
88        self.credentialsPlugin = None
89        self.authenticatorPlugin = None
90
91class SIRPPrincipal(Principal):
92    """A portal principal.
93
94    SIRP principals provide an extra `email` and `phone`
95    attribute extending ordinary principals.
96    """
97
98    grok.implements(ISIRPPrincipal)
99
100    def __init__(self, id, title=u'', description=u'', email=u'',
101                 phone=None, prefix=None):
102        self.id = id
103        if prefix is not None:
104            self.id = '%s.%s' % (prefix, self.id)
105        self.title = title
106        self.description = description
107        self.groups = []
108        self.email = email
109        self.phone = phone
110
111    def __repr__(self):
112        return 'SIRPPrincipal(%r)' % self.id
113
114class AuthenticatedSIRPPrincipalFactory(grok.MultiAdapter):
115    """Creates 'authenticated' SIRP principals.
116
117    Adapts (principal info, request) to a SIRPPrincipal instance.
118
119    This adapter is used by the standard PAU to transform
120    SIRPPrincipalInfos into SIRPPrincipal instances.
121    """
122    grok.adapts(ISIRPPrincipalInfo, IRequest)
123    grok.implements(IAuthenticatedPrincipalFactory)
124
125    def __init__(self, info, request):
126        self.info = info
127        self.request = request
128
129    def __call__(self, authentication):
130        principal = SIRPPrincipal(
131            self.info.id,
132            self.info.title,
133            self.info.description,
134            self.info.email,
135            self.info.phone,
136            authentication.prefix,
137            )
138        notify(
139            AuthenticatedPrincipalCreated(
140                authentication, principal, self.info, self.request))
141        return principal
142
143class Account(grok.Model):
144    grok.implements(IUserAccount)
145
146    _local_roles = dict()
147
148    def __init__(self, name, password, title=None, description=None,
149                 email=None, phone=None, roles = []):
150        self.name = name
151        if title is None:
152            title = name
153        #if description is None:
154        #    description = title
155        self.title = title
156        self.description = description
157        self.email = email
158        self.phone = phone
159        self.setPassword(password)
160        #self.setSiteRolesForPrincipal(roles)
161        # We don't want to share this dict with other accounts
162        self._local_roles = dict()
163
164    def setPassword(self, password):
165        passwordmanager = getUtility(IPasswordManager, 'SHA1')
166        self.password = passwordmanager.encodePassword(password)
167
168    def checkPassword(self, password):
169        passwordmanager = getUtility(IPasswordManager, 'SHA1')
170        return passwordmanager.checkPassword(self.password, password)
171
172    def getSiteRolesForPrincipal(self):
173        prm = get_principal_role_manager()
174        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
175                 if x[0].startswith('waeup.')]
176        return roles
177
178    def setSiteRolesForPrincipal(self, roles):
179        prm = get_principal_role_manager()
180        old_roles = self.getSiteRolesForPrincipal()
181        for role in old_roles:
182            # Remove old roles, not to be set now...
183            if role.startswith('waeup.') and role not in roles:
184                prm.unsetRoleForPrincipal(role, self.name)
185        for role in roles:
186            prm.assignRoleToPrincipal(role, self.name)
187
188    roles = property(getSiteRolesForPrincipal, setSiteRolesForPrincipal)
189
190    def getLocalRoles(self):
191        return self._local_roles
192
193    def notifyLocalRoleChanged(self, obj, role_id, granted=True):
194        objects = self._local_roles.get(role_id, [])
195        if granted and obj not in objects:
196            objects.append(obj)
197        if not granted and obj in objects:
198            objects.remove(obj)
199        self._local_roles[role_id] = objects
200        if len(objects) == 0:
201            del self._local_roles[role_id]
202        self._p_changed = True
203        return
204
205class UserAuthenticatorPlugin(grok.GlobalUtility):
206    grok.implements(IAuthenticatorPlugin)
207    grok.provides(IAuthenticatorPlugin)
208    grok.name('users')
209
210    def authenticateCredentials(self, credentials):
211        if not isinstance(credentials, dict):
212            return None
213        if not ('login' in credentials and 'password' in credentials):
214            return None
215        account = self.getAccount(credentials['login'])
216
217        if account is None:
218            return None
219        if not account.checkPassword(credentials['password']):
220            return None
221        return SIRPPrincipalInfo(id=account.name,
222                             title=account.title,
223                             description=account.description,
224                             email=account.email,
225                             phone=account.phone)
226
227    def principalInfo(self, id):
228        account = self.getAccount(id)
229        if account is None:
230            return None
231        return SIRPPrincipalInfo(id=account.name,
232                             title=account.title,
233                             description=account.description,
234                             email=account.email,
235                             phone=account.phone)
236
237    def getAccount(self, login):
238        # ... look up the account object and return it ...
239        userscontainer = self.getUsersContainer()
240        if userscontainer is None:
241            return
242        return userscontainer.get(login, None)
243
244    def addAccount(self, account):
245        userscontainer = self.getUsersContainer()
246        if userscontainer is None:
247            return
248        # XXX: complain if name already exists...
249        userscontainer.addAccount(account)
250
251    def addUser(self, name, password, title=None, description=None):
252        userscontainer = self.getUsersContainer()
253        if userscontainer is None:
254            return
255        userscontainer.addUser(name, password, title, description)
256
257    def getUsersContainer(self):
258        site = grok.getSite()
259        return site['users']
260
261class PasswordValidator(grok.GlobalUtility):
262
263  grok.implements(IPasswordValidator)
264
265  def validate_password(self, pw, pw_repeat):
266       errors = []
267       if len(pw) < 3:
268         errors.append('Password must have at least 3 chars.')
269       if pw != pw_repeat:
270         errors.append('Passwords do not match.')
271       return errors
272
273class LocalRoleSetEvent(object):
274
275    grok.implements(ILocalRoleSetEvent)
276
277    def __init__(self, object, role_id, principal_id, granted=True):
278        self.object = object
279        self.role_id = role_id
280        self.principal_id = principal_id
281        self.granted = granted
282
283@grok.subscribe(IUserAccount, grok.IObjectRemovedEvent)
284def handle_account_removed(account, event):
285    """When an account is removed, local roles might have to be deleted.
286    """
287    local_roles = account.getLocalRoles()
288    principal = account.name
289    for role_id, object_list in local_roles.items():
290        for object in object_list:
291            try:
292                role_manager = IPrincipalRoleManager(object)
293            except TypeError:
294                # No role manager, no roles to remove
295                continue
296            role_manager.unsetRoleForPrincipal(role_id, principal)
297    return
298
299@grok.subscribe(IUserAccount, grok.IObjectAddedEvent)
300def handle_account_added(account, event):
301    """When an account is added, the local owner role and the global
302    AcademicsOfficer role must be set.
303    """
304    # We set the local Owner role
305    role_manager_account = IPrincipalRoleManager(account)
306    role_manager_account.assignRoleToPrincipal(
307        'waeup.local.Owner', account.name)
308    # We set the global AcademicsOfficer role
309    site = grok.getSite()
310    role_manager_site = IPrincipalRoleManager(site)
311    role_manager_site.assignRoleToPrincipal(
312        'waeup.AcademicsOfficer', account.name)
313    # Finally we have to notify the user account that the local role
314    # of the same object has changed
315    notify(LocalRoleSetEvent(
316        account, 'waeup.local.Owner', account.name, granted=True))
317    return
318
319@grok.subscribe(Interface, ILocalRoleSetEvent)
320def handle_local_role_changed(obj, event):
321    site = grok.getSite()
322    if site is None:
323        return
324    users = site.get('users', None)
325    if users is None:
326        return
327    role_id = event.role_id
328    if event.principal_id not in users.keys():
329        return
330    user = users[event.principal_id]
331    user.notifyLocalRoleChanged(event.object, event.role_id, event.granted)
332    return
333
334@grok.subscribe(Interface, grok.IObjectRemovedEvent)
335def handle_local_roles_on_obj_removed(obj, event):
336    try:
337        role_map = IPrincipalRoleMap(obj)
338    except TypeError:
339        # no map, no roles to remove
340        return
341    for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
342        notify(LocalRoleSetEvent(
343                obj, local_role, user_name, granted=False))
344    return
Note: See TracBrowser for help on using the repository browser.