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

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

Fix imports of moved helper functions.

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