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

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

Now we have a configuration object and can provide ContactAdminForm? with proper credentials for a smtp server.

Add Email address to IAccount objects so that 'From' fields in emails, sent by users, can be automatically filled.

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