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

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

Make authentication finally work correctly after intensive logical
examination of all possible parameter combinations.

File size: 9.9 KB
Line 
1##
2## authentication.py
3## Login : <uli@pu.smp.net>
4## Started on  Tue Jul 27 14:26:35 2010 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2010 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"""Special authentication for applicants.
23
24   XXX: This is work in progress, experimental code! Don't do that at home!
25"""
26import grok
27from zope.event import notify
28from zope.pluggableauth.factories import Principal
29from zope.pluggableauth.interfaces import (
30    ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo,
31    IAuthenticatedPrincipalFactory, AuthenticatedPrincipalCreated)
32from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
33from zope.publisher.interfaces import IRequest
34from zope.publisher.interfaces.http import IHTTPRequest
35from zope.session.interfaces import ISession
36from waeup.sirp.authentication import Account, PrincipalInfo
37from waeup.sirp.jambtables.interfaces import (
38    IApplicantPrincipalInfo, IApplicantPrincipal, IApplicantSessionCredentials,
39    IJAMBApplicantSessionCredentials)
40from waeup.sirp.jambtables.util import get_applicant_data, application_exists
41from waeup.sirp.accesscodes import get_access_code
42
43class PortalUser(grok.Role):
44    """A role for applicants.
45    """
46    grok.name('waeup.Applicant')
47    grok.permissions('waeup.Public')
48
49class ApplicantAccount(Account):
50    """An account for applicants.
51
52    XXX: Very likely we do not need this at all.
53    """
54    def __init__(self, reg_no, ac):
55        self.name = u'applicant'
56        self.title = u'Applicant'
57        self.description = u'Applicant'
58        self.reg_no = reg_no
59        self.access_code = ac
60        roles = ['waeup.Applicant']
61        self.setRoles(roles)
62
63    def checkPassword(self, reg_no, ac):
64        """Check whether the given credentials exist.
65
66        `reg_no` is a JAMB registration number. `ac` an access code
67        string like ``PUDE-23-1234567890``.
68        """
69        if self.reg_no != reg_no or self.access_code != ac:
70            return False
71        return True
72        site = grok.getSite()
73        if not reg_no in site['applications']:
74            return False
75        data = get_applicant_data(reg_no, ac)
76        if data is None:
77            return False
78        return True
79
80class ApplicantPrincipalInfo(object):
81    """Infos about an applicant principal.
82    """
83    grok.implements(IApplicantPrincipalInfo)
84
85    # def __init__(self, id, title, description):
86    # def __init__(self, reg_no, access_code):
87    def __init__(self, access_code, jamb_reg_no=None):
88        self.id = principal_id(access_code, jamb_reg_no)
89        self.title = u'Applicant'
90        self.description = u'An Applicant'
91        self.credentialsPlugin = None
92        self.authenticatorPlugin = None
93        self.reg_no = jamb_reg_no
94        self.access_code = access_code
95
96class ApplicantPrincipal(Principal):
97    """An applicant principal.
98
99    Applicant principals provide an extra `access_code` and `reg_no`
100    attribute extending ordinary principals.
101    """
102
103    grok.implements(IApplicantPrincipal)
104
105    def __init__(self, reg_no, access_code):
106        self.id = '%s-%s' % (reg_no, access_code)
107        self.title = u'Applicant'
108        self.description = u'An applicant'
109        self.groups = []
110        self.reg_no = reg_no
111        self.access_code = access_code
112
113    def __repr__(self):
114        return 'ApplicantPrincipal(%r)' % self.id
115
116class AuthenticatedApplicantPrincipalFactory(grok.MultiAdapter):
117    """Creates 'authenticated' applicant principals.
118
119    Adapts (principal info, request) to an ApplicantPrincipal instance.
120
121    This adapter is used by the standard PAU to transform
122    PrincipalInfos into Principal instances.
123    """
124    grok.adapts(IApplicantPrincipalInfo, IRequest)
125    grok.implements(IAuthenticatedPrincipalFactory)
126
127    def __init__(self, info, request):
128        self.info = info
129        self.request = request
130
131    def __call__(self, authentication):
132        principal = ApplicantPrincipal(
133            authentication.prefix + self.info.reg_no,
134            self.info.access_code
135            )
136        notify(
137            AuthenticatedPrincipalCreated(
138                authentication, principal, self.info, self.request))
139        return principal
140
141
142#
143# Credentials plugins and related....
144#
145
146class ApplicantCredentials(object):
147    """Credentials class for ordinary applicants.
148    """
149    grok.implements(IApplicantSessionCredentials)
150
151    def __init__(self, access_code):
152        self.access_code = access_code
153
154    def getAccessCode(self):
155        """Get the access code.
156        """
157        return self.access_code
158
159    def getLogin(self):
160        """Stay compatible with non-applicant authenticators.
161        """
162        return None
163
164    def getPassword(self):
165        """Stay compatible with non-applicant authenticators.
166        """
167        return None
168   
169class JAMBApplicantCredentials(ApplicantCredentials):
170    """Credentials class for JAMB-screened applicants.
171    """
172    grok.implements(IJAMBApplicantSessionCredentials)
173
174    def __init__(self, access_code, jamb_reg_no):
175        self.access_code = access_code
176        self.jamb_reg_no = jamb_reg_no
177
178    def getJAMBRegNo(self):
179        """Get the JAMB registration no.
180        """
181        return self.jamb_reg_no
182
183class WAeUPApplicantCredentialsPlugin(grok.GlobalUtility,
184                                      SessionCredentialsPlugin):
185    """A credentials plugin that scans requests for applicant credentials.
186    """
187    grok.provides(ICredentialsPlugin)
188    grok.name('applicant_credentials')
189
190    loginpagename = 'login'
191    accesscode_prefix_field = 'form.prefix'
192    accesscode_series_field = 'form.ac_series'
193    accesscode_number_field = 'form.ac_number'
194    jamb_reg_no_field = 'form.jamb_reg_no'
195
196    def extractCredentials(self, request):
197        """Extracts credentials from a session if they exist.
198        """
199        if not IHTTPRequest.providedBy(request):
200            return None
201        session = ISession(request)
202        sessionData = session.get(
203            'zope.pluggableauth.browserplugins')
204        access_code_prefix = request.get(self.accesscode_prefix_field, None)
205        access_code_series = request.get(self.accesscode_series_field, None)
206        access_code_no = request.get(self.accesscode_number_field, None)
207        jamb_reg_no = request.get(self.jamb_reg_no_field, None)
208        access_code = '%s-%s-%s' % (
209            access_code_prefix, access_code_series, access_code_no)
210        if None in [access_code_prefix, access_code_series, access_code_no]:
211            access_code = None
212        credentials = None
213
214        if access_code and jamb_reg_no:
215            credentials = JAMBApplicantCredentials(
216                access_code, jamb_reg_no)
217        elif access_code:
218            credentials = ApplicantCredentials(access_code)
219        elif not sessionData:
220            return None
221        sessionData = session[
222            'zope.pluggableauth.browserplugins']
223        if credentials:
224            sessionData['credentials'] = credentials
225        else:
226            credentials = sessionData.get('credentials', None)
227        if not credentials:
228            return None
229        if not IJAMBApplicantSessionCredentials.providedBy(credentials):
230            # Entered credentials are ordinary applicant credentials,
231            # not JAMB-screened applicant credentials
232            return {'accesscode': credentials.getAccessCode()}
233        return {'accesscode': credentials.getAccessCode(),
234                'jambregno': credentials.getJAMBRegNo()}
235
236
237
238class ApplicantsAuthenticatorPlugin(grok.GlobalUtility):
239    """Authenticate applicants.
240
241    XXX: This plugin currently accepts any input and authenticates the
242    user as applicant.
243    """
244    grok.provides(IAuthenticatorPlugin)
245    grok.name('applicants')
246
247    def authenticateCredentials(self, credentials):
248        if not isinstance(credentials, dict):
249            return None
250        accesscode = credentials.get('accesscode', None)
251        jambregno = credentials.get('jambregno', None)
252        if accesscode is None:
253            return None
254        applicant_data, ac = get_applicant_data(jambregno, accesscode)
255        appl_ac = getattr(applicant_data, 'access_code', None)
256        #print "AUTH", accesscode, jambregno
257        if ac is None:
258            return None
259        if jambregno is not None and applicant_data is None:
260            return None
261        if ac.invalidation_date is not None and appl_ac != ac.representation:
262            return None
263        if appl_ac is not None and appl_ac != ac.representation:
264            return None
265        return ApplicantPrincipalInfo(accesscode, jambregno)
266
267    def principalInfo(self, id):
268        """Returns an IPrincipalInfo object for the specified principal id.
269
270        This method is used by the stadard PAU to lookup for instance
271        groups. If a principal belongs to a group, the group is looked
272        up by the id.  Currently we always return ``None``,
273        indicating, that the principal could not be found. This also
274        means, that is has no effect if applicant users belong to a
275        certain group. They can not gain extra-permissions this way.
276        """
277        return None
278
279def principal_id(access_code, jamb_reg_no=None):
280    """Get a principal ID for applicants.
281
282    We need unique principal ids for appliants. As access codes must
283    be unique we simply return them.
284    """
285    return access_code
Note: See TracBrowser for help on using the repository browser.