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

Last change on this file since 7221 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
Line 
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##
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                 email=None, 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.email = email
95        self.setPassword(password)
96        #self.setSiteRolesForPrincipal(roles)
97        # We don't want to share this dict with other accounts
98        self._local_roles = dict()
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
108    def getSiteRolesForPrincipal(self):
109        prm = get_principal_role_manager()
110        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
111                 if x[0].startswith('waeup.')]
112        return roles
113
114    def setSiteRolesForPrincipal(self, roles):
115        prm = get_principal_role_manager()
116        old_roles = self.getSiteRolesForPrincipal()
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
124    roles = property(getSiteRolesForPrincipal, setSiteRolesForPrincipal)
125
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]
138        self._p_changed = True
139        return
140
141class UserAuthenticatorPlugin(grok.GlobalUtility):
142    grok.implements(IAuthenticatorPlugin)
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,
158                             title=account.title,
159                             description=account.description)
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,
166                             title=account.title,
167                             description=account.description)
168
169    def getAccount(self, login):
170        # ... look up the account object and return it ...
171        userscontainer = self.getUsersContainer()
172        if userscontainer is None:
173            return
174        return userscontainer.get(login, None)
175
176    def addAccount(self, account):
177        userscontainer = self.getUsersContainer()
178        if userscontainer is None:
179            return
180        # XXX: complain if name already exists...
181        userscontainer.addAccount(account)
182
183    def addUser(self, name, password, title=None, description=None):
184        userscontainer = self.getUsersContainer()
185        if userscontainer is None:
186            return
187        userscontainer.addUser(name, password, title, description)
188
189    def getUsersContainer(self):
190        site = grok.getSite()
191        return site['users']
192
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
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
215@grok.subscribe(IUserAccount, grok.IObjectRemovedEvent)
216def handle_account_removed(account, event):
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
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
234    AcademicsOfficer role must be set.
235    """
236    # We set the local Owner role
237    role_manager_account = IPrincipalRoleManager(account)
238    role_manager_account.assignRoleToPrincipal(
239        'waeup.local.Owner', account.name)
240    # We set the global AcademicsOfficer role
241    site = grok.getSite()
242    role_manager_site = IPrincipalRoleManager(site)
243    role_manager_site.assignRoleToPrincipal(
244        'waeup.AcademicsOfficer', account.name)
245    # Finally we have to notify the user account that the local role
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.