source: main/waeup.sirp/branches/uli-studentpw/src/waeup/sirp/students/authentication.py @ 6717

Last change on this file since 6717 was 6713, checked in by uli, 14 years ago

Enable immediate authentication of student when password is reset.

File size: 6.9 KB
Line 
1##
2## authentication.py
3## Login : <uli@pu.smp.net>
4## Started on  Fri Sep  2 15:22:47 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""
23Authenticate students.
24"""
25import grok
26from zope.component import getUtility
27from zope.password.interfaces import IPasswordManager
28from zope.pluggableauth.interfaces import (
29    IAuthenticatorPlugin, ICredentialsPlugin)
30from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
31from waeup.sirp.authentication import PrincipalInfo, get_principal_role_manager
32from waeup.sirp.interfaces import IAuthPluginUtility, IUserAccount
33from waeup.sirp.students.interfaces import IStudent
34
35class StudentAccount(grok.Adapter):
36    """An adapter to turn student objects into accounts on-the-fly.
37    """
38    grok.context(IStudent)
39    grok.implements(IUserAccount)
40
41    @property
42    def name(self):
43        return self.context.student_id
44
45    @property
46    def password(self):
47        return getattr(self.context, 'password', None)
48
49    @property
50    def title(self):
51        return self.context.name
52
53    @property
54    def description(self):
55        return self.title
56
57    def _get_roles(self):
58        prm = get_principal_role_manager()
59        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
60                 if x[0].startswith('waeup.')]
61        return roles
62
63    def _set_roles(self, roles):
64        """Set roles for principal denoted by this account.
65        """
66        prm = get_principal_role_manager()
67        old_roles = self.roles
68        for role in old_roles:
69            # Remove old roles, not to be set now...
70            if role.startswith('waeup.') and role not in roles:
71                prm.unsetRoleForPrincipal(role, self.name)
72        for role in roles:
73            prm.assignRoleToPrincipal(role, self.name)
74        return
75
76    roles = property(_get_roles, _set_roles)
77
78    def setPassword(self, password):
79        """Set a password (LDAP-compatible) SSHA encoded.
80
81        We do not store passwords in plaintext. Encrypted password is
82        stored as unicode string.
83        """
84        passwordmanager = getUtility(IPasswordManager, 'SSHA')
85        self.context.password = u'%s' % (
86            passwordmanager.encodePassword(password))
87
88    def checkPassword(self, password):
89        """Check whether the given `password` matches the one stored.
90        """
91        if not isinstance(password, basestring):
92            return False
93        passwordmanager = getUtility(IPasswordManager, 'SSHA')
94        return passwordmanager.checkPassword(
95            self.context.password.encode('utf-8'), # turn unicode into bytes
96            password)
97
98class StudentsAuthenticatorPlugin(grok.GlobalUtility):
99    grok.implements(IAuthenticatorPlugin)
100    grok.provides(IAuthenticatorPlugin)
101    grok.name('students')
102
103    def authenticateCredentials(self, credentials):
104        """Authenticate `credentials`.
105
106        `credentials` is a tuple (login, password).
107
108        We look up students to find out whether a respective student
109        exists, then check the password and return the resulting
110        `PrincipalInfo` or ``None`` if no such student can be found.
111        """
112        if not isinstance(credentials, dict):
113            return None
114        if not ('login' in credentials and 'password' in credentials):
115            return None
116        account = self.getAccount(credentials['login'])
117
118        if account is None:
119            return None
120        if not account.checkPassword(credentials['password']):
121            # XXX: This is not a proper solution. Works, however, basically
122            if credentials.get('reset', False) is not True:
123                return None
124        return PrincipalInfo(id=account.name,
125                             title=account.title,
126                             description=account.description)
127
128    def principalInfo(self, id):
129        """Get a principal identified by `id`.
130
131        This one is required by IAuthenticatorPlugin.
132        """
133        account = self.getAccount(id)
134        if account is None:
135            return None
136        return PrincipalInfo(id=account.name,
137                             title=account.title,
138                             description=account.description)
139
140    def getAccount(self, login):
141        """Look up a student identified by `login`. Returns an account.
142
143        Currently, we simply look up the key under which the student
144        is stored in the portal. That means we hit if login name and
145        name under which the student is stored match.
146
147        Returns not a student but an account object adapted from any
148        student found.
149
150        If no such student exists, ``None`` is returned.
151        """
152        site = grok.getSite()
153        if site is None:
154            return None
155        studentscontainer = site.get('students', None)
156        if studentscontainer is None:
157            return None
158        student = studentscontainer.get(login, None)
159        if student is None:
160            return None
161        return IUserAccount(student)
162
163class PasswordChangeCredentialsPlugin(grok.GlobalUtility,
164                                      SessionCredentialsPlugin):
165    grok.provides(ICredentialsPlugin)
166    grok.name('student_pw_change')
167
168    loginpagename = 'login'
169    loginfield = 'student_id'
170    passwordfield = 'form.password'
171
172    def extractCredentials(self, request):
173        result = super(
174            PasswordChangeCredentialsPlugin, self).extractCredentials(
175            request)
176        if isinstance(result, dict):
177            # Add a marker for the authentication plugin
178            result['reset'] = True
179        return result
180
181class StudentsAuthenticatorSetup(grok.GlobalUtility):
182    """Register or unregister student authentication for a PAU.
183
184    This piece is called when a new site is created.
185    """
186    grok.implements(IAuthPluginUtility)
187
188    def register(self, pau):
189        plugins = list(pau.credentialsPlugins)
190        plugins.insert(0, 'student_pw_change')
191        pau.credentialsPlugins = tuple(plugins)
192        plugins = list(pau.authenticatorPlugins)
193        plugins.append('students')
194        pau.authenticatorPlugins = tuple(plugins)
195        return pau
196
197    def unregister(self, pau):
198        plugins = [x for x in pau.authenticatorPlugins
199                   if x != 'students']
200        pau.authenticatorPlugins = tuple(plugins)
201        return pau
Note: See TracBrowser for help on using the repository browser.