- Timestamp:
- 30 Nov 2011, 23:13:26 (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.sirp/trunk/src/waeup/sirp/applicants/authentication.py
r7235 r7240 1 1 ## $Id$ 2 ## 2 ## 3 3 ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann 4 4 ## This program is free software; you can redistribute it and/or modify … … 6 6 ## the Free Software Foundation; either version 2 of the License, or 7 7 ## (at your option) any later version. 8 ## 8 ## 9 9 ## This program is distributed in the hope that it will be useful, 10 10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 11 11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 12 ## GNU General Public License for more details. 13 ## 13 ## 14 14 ## You should have received a copy of the GNU General Public License 15 15 ## along with this program; if not, write to the Free Software 16 16 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 17 ## 18 """Special authentication for applicants. 19 20 XXX: This is work in progress, experimental code! Don't do that at home! 18 """ 19 Authenticate applicants. 21 20 """ 22 21 import grok 23 from zope. event import notify24 from zope.p luggableauth.factories import Principal22 from zope.component import getUtility 23 from zope.password.interfaces import IPasswordManager 25 24 from zope.pluggableauth.interfaces import ( 26 ICredentialsPlugin, IAuthenticatorPlugin, 27 IAuthenticatedPrincipalFactory, AuthenticatedPrincipalCreated) 28 from zope.pluggableauth.plugins.session import SessionCredentialsPlugin 29 from zope.publisher.interfaces import IRequest 25 IAuthenticatorPlugin, ICredentialsPlugin) 26 from zope.pluggableauth.plugins.session import ( 27 SessionCredentialsPlugin, SessionCredentials) 30 28 from zope.publisher.interfaces.http import IHTTPRequest 31 29 from zope.session.interfaces import ISession 32 from waeup.sirp.a ccesscodes import get_access_code33 from waeup.sirp. applicants.interfaces import (34 IA pplicantPrincipalInfo, IApplicantPrincipal, IApplicantSessionCredentials,35 ) 36 from waeup.sirp. applicants import get_applicant_data37 from waeup.sirp.interfaces import IAuthPluginUtility 30 from waeup.sirp.authentication import SIRPPrincipalInfo, get_principal_role_manager 31 from waeup.sirp.interfaces import ( 32 IAuthPluginUtility, IUserAccount, IPasswordValidator) 33 from waeup.sirp.applicants.interfaces import IApplicant 34 from waeup.sirp.students.authentication import ( 35 StudentAccount, StudentsAuthenticatorPlugin) 38 36 37 class ApplicantAccount(StudentAccount): 38 """An adapter to turn applicant objects into accounts on-the-fly. 39 """ 40 grok.context(IApplicant) 41 grok.implements(IUserAccount) 39 42 40 class ApplicantPrincipalInfo(object): 41 """Infos about an applicant principal. 42 """ 43 grok.implements(IApplicantPrincipalInfo) 43 @property 44 def name(self): 45 return self.context.applicant_id 44 46 45 def __init__(self, access_code): 46 self.id = principal_id(access_code) 47 self.title = u'Applicant' 48 self.description = u'An Applicant' 49 self.credentialsPlugin = None 50 self.authenticatorPlugin = None 51 self.access_code = access_code 47 @property 48 def title(self): 49 return self.context.fullname 52 50 53 class ApplicantPrincipal(Principal): 54 """An applicant principal. 51 @property 52 def user_type(self): 53 return u'applicant' 55 54 56 Applicant principals provide an extra `access_code` and `reg_no` 57 attribute extending ordinary principals. 58 """ 59 60 grok.implements(IApplicantPrincipal) 61 62 def __init__(self, access_code, prefix=None): 63 self.id = principal_id(access_code) 64 if prefix is not None: 65 self.id = '%s.%s' % (prefix, self.id) 66 self.title = u'Applicant' 67 self.description = u'An applicant' 68 self.groups = [] 69 self.access_code = access_code 70 71 def __repr__(self): 72 return 'ApplicantPrincipal(%r)' % self.id 73 74 class AuthenticatedApplicantPrincipalFactory(grok.MultiAdapter): 75 """Creates 'authenticated' applicant principals. 76 77 Adapts (principal info, request) to an ApplicantPrincipal instance. 78 79 This adapter is used by the standard PAU to transform 80 PrincipalInfos into Principal instances. 81 """ 82 grok.adapts(IApplicantPrincipalInfo, IRequest) 83 grok.implements(IAuthenticatedPrincipalFactory) 84 85 def __init__(self, info, request): 86 self.info = info 87 self.request = request 88 89 def __call__(self, authentication): 90 principal = ApplicantPrincipal( 91 self.info.access_code, 92 authentication.prefix, 93 ) 94 notify( 95 AuthenticatedPrincipalCreated( 96 authentication, principal, self.info, self.request)) 97 return principal 98 99 100 # 101 # Credentials plugins and related.... 102 # 103 104 class ApplicantCredentials(object): 105 """Credentials class for ordinary applicants. 106 """ 107 grok.implements(IApplicantSessionCredentials) 108 109 def __init__(self, access_code): 110 self.access_code = access_code 111 112 def getAccessCode(self): 113 """Get the access code. 114 """ 115 return self.access_code 116 117 def getLogin(self): 118 """Stay compatible with non-applicant authenticators. 119 """ 120 return None 121 122 def getPassword(self): 123 """Stay compatible with non-applicant authenticators. 124 """ 125 return None 126 127 class WAeUPApplicantCredentialsPlugin(grok.GlobalUtility, 128 SessionCredentialsPlugin): 129 """A credentials plugin that scans requests for applicant credentials. 130 """ 131 grok.provides(ICredentialsPlugin) 132 grok.name('applicant_credentials') 133 134 loginpagename = 'login' 135 accesscode_prefix_field = 'form.ac_prefix' 136 accesscode_series_field = 'form.ac_series' 137 accesscode_number_field = 'form.ac_number' 138 139 def extractCredentials(self, request): 140 """Extracts credentials from a session if they exist. 141 """ 142 if not IHTTPRequest.providedBy(request): 143 return None 144 session = ISession(request) 145 sessionData = session.get( 146 'zope.pluggableauth.browserplugins') 147 access_code_prefix = request.get(self.accesscode_prefix_field, None) 148 access_code_series = request.get(self.accesscode_series_field, None) 149 access_code_no = request.get(self.accesscode_number_field, None) 150 access_code = '%s-%s-%s' % ( 151 access_code_prefix, access_code_series, access_code_no) 152 if None in [access_code_prefix, access_code_series, access_code_no]: 153 access_code = None 154 credentials = None 155 156 if access_code: 157 credentials = ApplicantCredentials(access_code) 158 elif not sessionData: 159 return None 160 sessionData = session[ 161 'zope.pluggableauth.browserplugins'] 162 if credentials: 163 sessionData['credentials'] = credentials 164 else: 165 credentials = sessionData.get('credentials', None) 166 if not credentials: 167 return None 168 if not IApplicantSessionCredentials.providedBy(credentials): 169 # If credentials were stored in session from another 170 # credentials plugin then we cannot make assumptions about 171 # its structure. 172 return None 173 return {'accesscode': credentials.getAccessCode()} 174 175 176 177 class ApplicantsAuthenticatorPlugin(grok.GlobalUtility): 178 """Authenticate applicants. 179 """ 55 class ApplicantsAuthenticatorPlugin(StudentsAuthenticatorPlugin): 56 grok.implements(IAuthenticatorPlugin) 180 57 grok.provides(IAuthenticatorPlugin) 181 58 grok.name('applicants') 182 59 183 def authenticateCredentials(self, credentials):184 """ Validate the given `credentials`60 def getAccount(self, login): 61 """Look up a applicant identified by `login`. Returns an account. 185 62 186 Credentials for applicants have to be passed as a regular 187 dictionary with a key ``accesscode``. This access code is the 188 password and username of an applicant. 63 First we split the login name into the container part and 64 the application number part. Then we simply look up the key under which 65 the applicant is stored in the respective applicants cointainer of 66 the portal. 189 67 190 Returns a :class:`ApplicantPrincipalInfo` in case of191 successful validation, ``None`` else.68 Returns not an applicant but an account object adapted from any 69 applicant found. 192 70 193 Credentials are not valid if: 194 195 - The passed accesscode does not exist (i.e. was not generated 196 by the :mod:`waeup.sirp.accesscode` module). 197 198 or 199 200 - the accesscode was disabled 201 202 or 203 204 - the accesscode was already used and a dataset for this 205 applicant was already generated with a different accesscode 206 (currently impossible, as applicant datasets are indexed by 207 accesscode) 208 209 or 210 211 - a dataset for the applicant already exists with an 212 accesscode set and this accesscode does not match the given 213 one. 214 71 If no such applicant exists, ``None`` is returned. 215 72 """ 216 if not isinstance(credentials, dict): 73 site = grok.getSite() 74 if site is None: 217 75 return None 218 a ccesscode = credentials.get('accesscode', None)219 if a ccesscodeis None:76 applicantsroot = site.get('applicants', None) 77 if applicantsroot is None: 220 78 return None 221 applicant_data = get_applicant_data(accesscode) 222 ac = get_access_code(accesscode) # Get the real access code object 223 appl_ac = getattr(applicant_data, 'access_code', None) 224 if ac is None: 79 try: 80 container, application_number = login.split('_') 81 except ValueError: 225 82 return None 226 if ac.state == 'disabled': 83 applicantscontainer = applicantsroot.get(container,None) 84 if applicantscontainer is None: 227 85 return None 228 if ac.state == 'used' and appl_ac != ac.representation: 86 applicant = applicantscontainer.get(application_number, None) 87 if applicant is None: 229 88 return None 230 # If the following fails we have a catalog error. Bad enough 231 # to pull emergency break. 232 assert appl_ac is None or appl_ac == ac.representation 233 return ApplicantPrincipalInfo(accesscode) 234 235 def principalInfo(self, id): 236 """Returns an IPrincipalInfo object for the specified principal id. 237 238 This method is used by the stadard PAU to lookup for instance 239 groups. If a principal belongs to a group, the group is looked 240 up by the id. Currently we always return ``None``, 241 indicating, that the principal could not be found. This also 242 means, that is has no effect if applicant users belong to a 243 certain group. They can not gain extra-permissions this way. 244 """ 245 return None 89 return IUserAccount(applicant) 246 90 247 91 class ApplicantsAuthenticatorSetup(grok.GlobalUtility): 248 """ A global utility that sets up any PAU passed.92 """Register or unregister applicant authentication for a PAU. 249 93 250 The methods of this utility are called during setup of a new site 251 (`University`) instance and after the regular authentication 252 systems (regular users, officers, etc.) were set up. 94 This piece is called when a new site is created. 253 95 """ 254 96 grok.implements(IAuthPluginUtility) … … 256 98 257 99 def register(self, pau): 258 """Register our local applicants specific PAU components. 259 260 Applicants provide their own authentication system resulting 261 in a specialized credentials plugin and a specialized 262 authenticator plugin. 263 264 Here we tell a given PAU that these plugins exist and should 265 be consulted when trying to authenticate a user. 266 267 We stack our local plugins at end of the plugin list, so that 268 other authentication mechanisms (the normal user 269 authentication for instance) have precedence and to avoid 270 "account-shadowing". 271 """ 272 # The local credentials plugin is registered under the name 273 # 'applicant_credentials' (see above). 274 plugins = list(pau.credentialsPlugins) + ['applicant_credentials'] 275 pau.credentialsPlugins = tuple(plugins) 276 # The local authenticator plugin is registered under the name 277 # 'applicants' (subject to change?) 278 plugins = list(pau.authenticatorPlugins) + ['applicants'] 100 plugins = list(pau.authenticatorPlugins) 101 plugins.append('applicants') 279 102 pau.authenticatorPlugins = tuple(plugins) 280 103 return pau 281 104 282 105 def unregister(self, pau): 283 """Unregister applicant specific authentication components from PAU. 284 """ 285 pau.credentialsPlugins = tuple( 286 [x for x in list(pau.credentialsPlugins) 287 if x != 'applicant_credentials']) 288 pau.authenticatorPlugins = tuple( 289 [x for x in list(pau.authenticatorPlugins) 290 if x != 'applicants']) 106 plugins = [x for x in pau.authenticatorPlugins 107 if x != 'applicants'] 108 pau.authenticatorPlugins = tuple(plugins) 291 109 return pau 292 293 294 def principal_id(access_code):295 """Get a principal ID for applicants.296 297 We need unique principal ids for appliants. As access codes must298 be unique we simply return them.299 """300 return access_code
Note: See TracChangeset for help on using the changeset viewer.