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

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

Make applicant credentials work also if other credentials plugins
stored a different structured credentials set in session.

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