## ## authentication.py ## Login : ## Started on Tue Jul 27 14:26:35 2010 Uli Fouquet ## $Id$ ## ## Copyright (C) 2010 Uli Fouquet ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Special authentication for applicants. XXX: This is work in progress, experimental code! Don't do that at home! """ import grok from zope.event import notify from zope.pluggableauth.factories import Principal from zope.pluggableauth.interfaces import ( ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo, IAuthenticatedPrincipalFactory, AuthenticatedPrincipalCreated) from zope.pluggableauth.plugins.session import SessionCredentialsPlugin from zope.publisher.interfaces import IRequest from zope.publisher.interfaces.http import IHTTPRequest from zope.session.interfaces import ISession from waeup.sirp.authentication import Account, PrincipalInfo from waeup.sirp.jambtables.interfaces import ( IApplicantPrincipalInfo, IApplicantPrincipal, IApplicantSessionCredentials, IJAMBApplicantSessionCredentials) from waeup.sirp.jambtables.util import get_applicant_data, get_access_code class PortalUser(grok.Role): """A role for applicants. """ grok.name('waeup.Applicant') grok.permissions('waeup.Public') class ApplicantAccount(Account): """An account for applicants. XXX: Very likely we do not need this at all. """ def __init__(self, reg_no, ac): self.name = u'applicant' self.title = u'Applicant' self.description = u'Applicant' self.reg_no = reg_no self.access_code = ac roles = ['waeup.Applicant'] self.setRoles(roles) def checkPassword(self, reg_no, ac): """Check whether the given credentials exist. `reg_no` is a JAMB registration number. `ac` an access code string like ``PUDE-23-1234567890``. """ if self.reg_no != reg_no or self.access_code != ac: return False return True site = grok.getSite() if not reg_no in site['applications']: return False data = get_applicant_data(reg_no, ac) if data is None: return False return True class ApplicantPrincipalInfo(object): """Infos about an applicant principal. """ grok.implements(IApplicantPrincipalInfo) # def __init__(self, id, title, description): # def __init__(self, reg_no, access_code): def __init__(self, access_code, jamb_reg_no=None): self.id = principal_id(access_code, jamb_reg_no) self.title = u'Applicant' self.description = u'An Applicant' self.credentialsPlugin = None self.authenticatorPlugin = None self.reg_no = jamb_reg_no self.access_code = access_code class ApplicantPrincipal(Principal): """An applicant principal. Applicant principals provide an extra `access_code` and `reg_no` attribute extending ordinary principals. """ grok.implements(IApplicantPrincipal) def __init__(self, reg_no, access_code): self.id = '%s-%s' % (reg_no, access_code) self.title = u'Applicant' self.description = u'An applicant' self.groups = [] self.reg_no = reg_no self.access_code = access_code def __repr__(self): return 'ApplicantPrincipal(%r)' % self.id class AuthenticatedApplicantPrincipalFactory(grok.MultiAdapter): """Creates 'authenticated' applicant principals. Adapts (principal info, request) to an ApplicantPrincipal instance. This adapter is used by the standard PAU to transform PrincipalInfos into Principal instances. """ grok.adapts(IApplicantPrincipalInfo, IRequest) grok.implements(IAuthenticatedPrincipalFactory) def __init__(self, info, request): self.info = info self.request = request def __call__(self, authentication): principal = ApplicantPrincipal( authentication.prefix + self.info.reg_no, self.info.access_code ) notify( AuthenticatedPrincipalCreated( authentication, principal, self.info, self.request)) return principal # # Credentials plugins and related.... # class ApplicantCredentials(object): """Credentials class for ordinary applicants. """ grok.implements(IApplicantSessionCredentials) def __init__(self, access_code): self.access_code = access_code def getAccessCode(self): """Get the access code. """ return self.access_code def getLogin(self): """Stay compatible with non-applicant authenticators. """ return None def getPassword(self): """Stay compatible with non-applicant authenticators. """ return None class JAMBApplicantCredentials(ApplicantCredentials): """Credentials class for JAMB-screened applicants. """ grok.implements(IJAMBApplicantSessionCredentials) def __init__(self, access_code, jamb_reg_no): self.access_code = access_code self.jamb_reg_no = jamb_reg_no def getJAMBRegNo(self): """Get the JAMB registration no. """ return self.jamb_reg_no class WAeUPApplicantCredentialsPlugin(grok.GlobalUtility, SessionCredentialsPlugin): """A credentials plugin that scans requests for applicant credentials. """ grok.provides(ICredentialsPlugin) grok.name('applicant_credentials') loginpagename = 'login' accesscode_prefix_field = 'form.prefix' accesscode_series_field = 'form.ac_series' accesscode_number_field = 'form.ac_number' jamb_reg_no_field = 'form.jamb_reg_no' def extractCredentials(self, request): """Extracts credentials from a session if they exist. """ if not IHTTPRequest.providedBy(request): return None session = ISession(request) sessionData = session.get( 'zope.pluggableauth.browserplugins') access_code_prefix = request.get(self.accesscode_prefix_field, None) access_code_series = request.get(self.accesscode_series_field, None) access_code_no = request.get(self.accesscode_number_field, None) jamb_reg_no = request.get(self.jamb_reg_no_field, None) access_code = '%s-%s-%s' % ( access_code_prefix, access_code_series, access_code_no) if None in [access_code_prefix, access_code_series, access_code_no]: access_code = None credentials = None print "CRED2: %s, %s" % (access_code, jamb_reg_no) if access_code and jamb_reg_no: credentials = JAMBApplicantCredentials( access_code, jamb_reg_no) elif access_code: credentials = ApplicantCredentials(access_code) elif not sessionData: return None sessionData = session[ 'zope.pluggableauth.browserplugins'] if credentials: sessionData['credentials'] = credentials else: credentials = sessionData.get('credentials', None) if not credentials: return None if credentials.getJAMBRegNo() is not None: return {'accesscode': credentials.getAccessCode(), 'jambregno': credentials.getJAMBRegNo()} return {'accesscode': credentials.getAccessCode()} class ApplicantsAuthenticatorPlugin(grok.GlobalUtility): """Authenticate applicants. XXX: This plugin currently accepts any input and authenticates the user as applicant. """ grok.provides(IAuthenticatorPlugin) grok.name('applicants') def authenticateCredentials(self, credentials): if not isinstance(credentials, dict): return None if not 'accesscode' in credentials.keys(): return None if not 'jambregno' in credentials.keys(): credentials['jambregno'] = None ac = get_access_code(credentials['accesscode']) if ac is None: # Invalid access code entered. # XXX: log this. return None return ApplicantPrincipalInfo( credentials['accesscode'], credentials['jambregno'] ) def principalInfo(self, id): """Returns an IPrincipalInfo object for the specified principal id. This method is used by the stadard PAU to lookup for instance groups. If a principal belongs to a group, the group is looked up by the id. Currently we always return ``None``, indicating, that the principal could not be found. This also means, that is has no effect if applicant users belong to a certain group. They can not gain extra-permissions this way. """ return None def principal_id(access_code, jamb_reg_no=None): """Get a principal ID for applicants. We need unique principal ids for appliants. As access codes must be unique we simply return them. """ return access_code