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

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

More docs.

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