Changeset 7240 for main/waeup.sirp/trunk/src/waeup
- Timestamp:
- 30 Nov 2011, 23:13:26 (13 years ago)
- Location:
- main/waeup.sirp/trunk/src/waeup/sirp
- Files:
-
- 2 deleted
- 27 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.sirp/trunk/src/waeup/sirp/applicants/__init__.py
r7159 r7240 3 3 # Make this a package. 4 4 from waeup.sirp.applicants.applicant import ( 5 ResultEntry,Applicant, ApplicantFactory, ApplicantImageStoreHandler,6 get_regno_or_ac,ApplicantImageNameChooser,5 Applicant, ApplicantFactory, ApplicantImageStoreHandler, 6 ApplicantImageNameChooser, 7 7 ) 8 8 from waeup.sirp.applicants.container import ApplicantsContainer … … 14 14 15 15 __all__ = [ 16 'ResultEntry',17 16 'Applicant', 18 17 'ApplicantFactory', -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py
r7192 r7240 17 17 ## 18 18 import os 19 from random import SystemRandom as r 19 20 import grok 20 21 from grok import index 21 22 from zope.component.interfaces import IFactory 23 from zope.securitypolicy.interfaces import IPrincipalRoleManager 22 24 from zope.interface import implementedBy 23 25 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState … … 27 29 from waeup.sirp.interfaces import ( 28 30 IObjectHistory, IFileStoreHandler, IFileStoreNameChooser) 29 from waeup.sirp.utils.helpers import attrs_to_fields 31 from waeup.sirp.utils.helpers import attrs_to_fields, get_current_principal 30 32 from waeup.sirp.applicants.interfaces import ( 31 I ResultEntry, IApplicant, IApplicantEdit,33 IApplicant, IApplicantEdit, 32 34 ) 33 34 35 def get_regno_or_ac(context): 36 reg_no = getattr(context, 'reg_no', None) 37 if reg_no is None: 38 return getattr(context, 'access_code', None) 39 return reg_no 40 41 class ResultEntry(grok.Context): 42 grok.implements(IResultEntry) 43 44 def __init__(self, subject=None, score=None): 45 self.subject = subject 46 self.score = score 35 from waeup.sirp.applicants.workflow import INITIALIZED 36 37 def generate_applicant_id(container=None): 38 if container is not None: 39 aid = u"%s_%d" % (container.code, r().randint(99999,1000000)) 40 while aid in container.keys(): 41 aid = u"%s_%d" % (container.code, r().randint(99999,1000000)) 42 return aid 43 else: 44 # In some tests we don't use containers 45 return u"xxx_1234" 47 46 48 47 class Applicant(grok.Model): … … 50 49 grok.provides(IApplicant) 51 50 52 def __init__(self ):51 def __init__(self, container=None): 53 52 super(Applicant, self).__init__() 53 self.applicant_id = generate_applicant_id(container) 54 self.password = None 54 55 IWorkflowInfo(self).fireTransition('init') 55 56 self.application_date = None … … 69 70 history = IObjectHistory(self) 70 71 return history 72 73 @property 74 def application_number(self): 75 return self.applicant_id.split('_')[1] 76 77 @property 78 def fullname(self): 79 # We do not necessarily have the middlenames attribute 80 middlenames = getattr(self, 'middlenames', None) 81 if middlenames: 82 return '%s %s %s' % (self.firstname, 83 middlenames, self.lastname) 84 else: 85 return '%s %s' % (self.firstname, self.lastname) 71 86 72 87 # Set all attributes of Applicant required in IApplicant as field … … 84 99 85 100 access_code = index.Field(attribute='access_code') 101 applicant_id = index.Field(attribute='applicant_id') 102 reg_no = index.Field(attribute='reg_no') 86 103 87 104 class ApplicantFactory(grok.GlobalUtility): … … 94 111 95 112 def __call__(self, *args, **kw): 96 return Applicant( )113 return Applicant(kw['container']) 97 114 98 115 def getInterfaces(self): … … 147 164 *Example:* 148 165 149 For an applicant with registration no. ``'My_reg_no_1234'``166 For an applicant with applicant_id. ``'app2001_1234'`` 150 167 and stored in an applicants container called 151 168 ``'mycontainer'``, this chooser would create: 152 169 153 ``'__img-applicant__mycontainer/ My_reg_no_1234.jpg'``170 ``'__img-applicant__mycontainer/app2001_1234.jpg'`` 154 171 155 172 meaning that the passport image of this applicant would be 156 173 stored in the site-wide file storage in path: 157 174 158 ``mycontainer/ My_reg_no_1234.jpg``175 ``mycontainer/app2001_1234.jpg`` 159 176 160 177 If the context applicant has no parent, ``'_default'`` is used … … 171 188 marked_filename = '__%s__%s/%s.jpg' % ( 172 189 APPLICANT_IMAGE_STORE_NAME, 173 parent_name, get_regno_or_ac(self.context))190 parent_name, self.context.applicant_id) 174 191 return marked_filename 175 192 … … 214 231 return file, path, WAeUPImageFile( 215 232 file_obj.filename, file_obj.data) 233 234 @grok.subscribe(IApplicant, grok.IObjectAddedEvent) 235 def handle_applicant_added(applicant, event): 236 """If an applicant is added local and site roles are assigned. 237 """ 238 role_manager = IPrincipalRoleManager(applicant) 239 role_manager.assignRoleToPrincipal( 240 'waeup.local.ApplicationOwner', applicant.applicant_id) 241 # Assign current principal the global Applicant role 242 role_manager = IPrincipalRoleManager(grok.getSite()) 243 role_manager.assignRoleToPrincipal( 244 'waeup.Applicant', applicant.applicant_id) 245 246 # Assign global applicant role for new applicant (alternative way) 247 #account = IUserAccount(applicant) 248 #account.roles = ['waeup.Applicant'] 249 250 return 251 252 @grok.subscribe(IApplicant, grok.IObjectRemovedEvent) 253 def handle_applicant_removed(applicant, event): 254 """If an applicant is removed a message is logged. 255 """ 256 comment = 'Applicant record removed' 257 target = applicant.applicant_id 258 # In some tests we don't have a principal 259 try: 260 user = get_current_principal().id 261 except (TypeError, AttributeError): 262 return 263 try: 264 grok.getSite()['applicants'].logger.info('%s - %s - %s' % ( 265 user, target, comment)) 266 except KeyError: 267 # If we delete an entire university instance there won't be 268 # an applicants subcontainer 269 return 270 return -
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 -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py
r7237 r7240 24 24 from datetime import datetime, date 25 25 from zope.authentication.interfaces import ILogout, IAuthentication 26 from zope.component import getUtility 26 from zope.component import getUtility, createObject 27 27 from zope.formlib.widget import CustomWidgetFactory 28 28 from zope.formlib.form import setUpEditWidgets … … 51 51 ) 52 52 from waeup.sirp.interfaces import ( 53 IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser) 53 IWAeUPObject, ILocalRolesAssignable, IExtFileStore, 54 IFileStoreNameChooser, IPasswordValidator, IUserAccount) 54 55 from waeup.sirp.permissions import get_users_with_local_roles 55 56 from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH … … 61 62 from waeup.sirp.widgets.objectwidget import ( 62 63 WAeUPObjectWidget, WAeUPObjectDisplayWidget) 63 from waeup.sirp.applicants import ResultEntry,Applicant, get_applicant_data64 from waeup.sirp.applicants import Applicant, get_applicant_data 64 65 from waeup.sirp.applicants.interfaces import ( 65 IApplicant, IApplicant Principal,IApplicantEdit, IApplicantsRoot,66 IApplicant, IApplicantEdit, IApplicantsRoot, 66 67 IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab, 67 68 MAX_UPLOAD_SIZE, 68 69 ) 69 70 from waeup.sirp.applicants.workflow import INITIALIZED, STARTED 70 71 results_widget = CustomWidgetFactory( 72 WAeUPObjectWidget, ResultEntry) 73 74 results_display_widget = CustomWidgetFactory( 75 WAeUPObjectDisplayWidget, ResultEntry) 71 from waeup.sirp.students.viewlets import PrimaryStudentNavTab 72 73 grok.context(IWAeUPObject) # Make IWAeUPObject the default context 76 74 77 75 class ApplicantsRootPage(WAeUPPage): … … 228 226 """Get a title for a context. 229 227 """ 230 return self.context.a ccess_code228 return self.context.application_number 231 229 232 230 class ApplicantsAuthTab(PrimaryNavTab): … … 235 233 grok.context(IWAeUPObject) 236 234 grok.order(3) 237 grok.require('waeup.viewApplication ')235 grok.require('waeup.viewApplicationsTab') 238 236 grok.template('primarynavtab') 239 237 pnav = 3 … … 263 261 # return tt 264 262 263 class MyApplicationDataTab(PrimaryStudentNavTab): 264 """MyData-tab in primary navigation. 265 """ 266 grok.order(3) 267 grok.require('waeup.viewMyApplicationDataTab') 268 grok.template('primarynavtab') 269 pnav = 3 270 tab_title = u'My Data' 271 272 @property 273 def link_target(self): 274 try: 275 container, application_number = self.request.principal.id.split('_') 276 except ValueError: 277 return 278 rel_link = '/applicants/%s/%s' % (container, application_number) 279 return self.view.application_url() + rel_link 280 265 281 class ApplicantsContainerPage(WAeUPDisplayFormPage): 266 282 """The standard view for regular applicant containers. … … 292 308 text = 'Manage applicants container' 293 309 294 class ApplicantLoginActionButton(ManageActionButton):295 grok.order(2)296 grok.context(IApplicantsContainer)297 grok.view(ApplicantsContainerPage)298 grok.require('waeup.Anonymous')299 icon = 'login.png'300 text = 'Login for applicants'301 target = 'login'310 #class ApplicantLoginActionButton(ManageActionButton): 311 # grok.order(2) 312 # grok.context(IApplicantsContainer) 313 # grok.view(ApplicantsContainerPage) 314 # grok.require('waeup.Anonymous') 315 # icon = 'login.png' 316 # text = 'Login for applicants' 317 # target = 'login' 302 318 303 319 class ApplicantsContainerManageFormPage(WAeUPEditFormPage): … … 394 410 return del_local_roles(self,3,**data) 395 411 412 # Not used anymore 396 413 class ApplicantLoginPage(WAeUPPage): 397 414 grok.context(IApplicantsContainer) … … 421 438 self.flash('Entered credentials are invalid.') 422 439 return 423 if not IApplicantPrincipal.providedBy(self.request.principal):424 # Don't care if user is already authenticated as non-applicant440 # if not IApplicantPrincipal.providedBy(self.request.principal): 441 # # Don't care if user is already authenticated as non-applicant 425 442 return 426 443 … … 470 487 471 488 # Mark application as started 472 if IWorkflowState(self.context[pin]).getState() is INITIALIZED:473 IWorkflowInfo(self.context[pin]).fireTransition('start')489 #if IWorkflowState(self.context[pin]).getState() is INITIALIZED: 490 # IWorkflowInfo(self.context[pin]).fireTransition('start') 474 491 475 492 self.redirect(self.url(self.context[pin], 'edit')) 493 return 494 495 def render(self): 476 496 return 477 497 … … 482 502 grok.require('waeup.manageApplication') 483 503 grok.name('addapplicant') 484 grok.template('applicantaddpage') 504 #grok.template('applicantaddpage') 505 form_fields = grok.AutoFields(IApplicant).select( 506 'firstname', 'middlenames', 'lastname', 507 'email', 'phone') 485 508 title = 'Applicants' 486 509 label = 'Add applicant' … … 491 514 return "Applicants Container: %s" % self.context.title 492 515 493 @property494 def ac_prefix(self):495 return self.context.ac_prefix496 497 516 @grok.action('Create application record') 498 517 def addApplicant(self, **data): 499 ac_series = self.request.form.get('form.ac_series', None) 500 ac_number = self.request.form.get('form.ac_number', None) 501 pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number) 502 if not invalidate_accesscode(pin, comment=u"Invalidated by system"): 503 self.flash('%s is not a valid access code.' % pin) 504 self.redirect(self.url(self.context, '@@manage')+'#tab-2') 505 return 506 else: 507 # Create applicant record 508 applicant = Applicant() 509 applicant.access_code = pin 510 self.context[pin] = applicant 511 self.redirect(self.url(self.context[pin], 'manage')) 512 return 513 514 class AccessCodeViewLink(LeftSidebarLink): 515 grok.order(1) 516 grok.require('waeup.Public') 517 icon = 'actionicon_view.png' 518 title = 'View Record' 519 target = '/@@index' 520 521 @property 522 def url(self): 523 if not IApplicantPrincipal.providedBy(self.request.principal): 524 return '' 525 access_code = getattr(self.request.principal,'access_code',None) 526 if access_code: 527 applicant_object = get_applicant_data(access_code) 528 return absoluteURL(applicant_object, self.request) + self.target 529 return '' 530 531 class AccessCodeEditLink(AccessCodeViewLink): 532 grok.order(2) 533 grok.require('waeup.Public') 534 icon = 'actionicon_modify.png' 535 title = 'Edit Record' 536 target = '/@@edit' 537 538 @property 539 def url(self): 540 if not IApplicantPrincipal.providedBy(self.request.principal): 541 return '' 542 access_code = getattr(self.request.principal,'access_code',None) 543 if access_code: 544 applicant_object = get_applicant_data(access_code) 545 if applicant_object.locked: 546 return '' 547 return absoluteURL(applicant_object, self.request) + self.target 548 return '' 549 550 class AccessCodeSlipLink(AccessCodeViewLink): 551 grok.order(3) 552 grok.require('waeup.Public') 553 icon = 'actionicon_pdf.png' 554 title = 'Download Slip' 555 target = '/application_slip.pdf' 518 applicant = createObject(u'waeup.Applicant', container = self.context) 519 self.applyData(applicant, **data) 520 self.context.addApplicant(applicant) 521 self.flash('Applicant record created.') 522 self.redirect(self.url(self.context[applicant.application_number], 'index')) 523 return 556 524 557 525 class ApplicantDisplayFormPage(WAeUPDisplayFormPage): … … 561 529 grok.template('applicantdisplaypage') 562 530 form_fields = grok.AutoFields(IApplicant).omit( 563 'locked' ).omit('course_admitted')531 'locked', 'course_admitted', 'password') 564 532 form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le') 565 533 label = 'Applicant' … … 568 536 def update(self): 569 537 self.passport_url = self.url(self.context, 'passport.jpg') 570 return 538 # Mark application as started if applicant logs in for the first time 539 if IWorkflowState(self.context).getState() == INITIALIZED: 540 IWorkflowInfo(self.context).fireTransition('start') 541 return 542 543 @property 544 def hasPassword(self): 545 if self.context.password: 546 return 'set' 547 return 'unset' 571 548 572 549 @property 573 550 def title(self): 574 if self.request.principal.title == 'Applicant': 575 return u'Your Application Record' 576 return '%s' % self.context.access_code 551 return 'Application Record %s' % self.context.application_number 577 552 578 553 @property 579 554 def label(self): 580 555 container_title = self.context.__parent__.title 581 return '%s Application Record' % container_title 556 return '%s Application Record %s' % ( 557 container_title, self.context.application_number) 582 558 583 559 def getCourseAdmitted(self): … … 607 583 grok.require('waeup.viewApplication') 608 584 form_fields = grok.AutoFields(IApplicant).omit( 609 'locked' ).omit('course_admitted')585 'locked', 'course_admitted') 610 586 form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le') 611 587 prefix = 'form' … … 614 590 def label(self): 615 591 container_title = self.context.__parent__.title 616 return '%s Application Record' % container_title 592 return '%s Application Record %s' % ( 593 container_title, self.context.application_number) 617 594 618 595 def getCourseAdmitted(self): … … 710 687 target = 'manage' 711 688 689 class ApplicantEditActionButton(ManageActionButton): 690 grok.context(IApplicant) 691 grok.view(ApplicantDisplayFormPage) 692 grok.require('waeup.handleApplication') 693 text = 'Edit application record' 694 target ='edit' 695 696 @property 697 def target_url(self): 698 """Get a URL to the target... 699 """ 700 if self.context.locked: 701 return 702 return self.view.url(self.view.context, self.target) 712 703 713 704 def handle_img_upload(upload, context, view): … … 756 747 @property 757 748 def title(self): 758 return self.context.access_code749 return 'Application Record %s' % self.context.application_number 759 750 760 751 @property 761 752 def label(self): 762 753 container_title = self.context.__parent__.title 763 return '%s Application Form' % container_title 754 return '%s Application Form %s' % ( 755 container_title, self.context.application_number) 764 756 765 757 def getTransitions(self): … … 776 768 @grok.action('Save') 777 769 def save(self, **data): 770 form = self.request.form 771 password = form.get('password', None) 772 password_ctl = form.get('control_password', None) 773 if password: 774 validator = getUtility(IPasswordValidator) 775 errors = validator.validate_password(password, password_ctl) 776 if errors: 777 self.flash( ' '.join(errors)) 778 return 778 779 if self.passport_changed is False: # False is not None! 779 780 return # error during image upload. Ignore other values … … 782 783 if changed_fields: 783 784 changed_fields = reduce(lambda x,y: x+y, changed_fields.values()) 785 else: 786 changed_fields = [] 787 if self.passport_changed: 788 changed_fields.append('passport') 789 if password: 790 # Now we know that the form has no errors and can set password ... 791 IUserAccount(self.context).setPassword(password) 792 changed_fields.append('password') 784 793 fields_string = ' + '.join(changed_fields) 785 if self.passport_changed:786 fields_string += ' + passport'787 #self.context._p_changed = True788 form = self.request.form789 794 trans_id = form.get('transition', None) 790 795 if trans_id: … … 804 809 form_fields = grok.AutoFields(IApplicantEdit).omit( 805 810 'locked', 'course_admitted', 'student_id', 806 'screening_score', 811 'screening_score', 'applicant_id' 807 812 ) 808 813 form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year') … … 863 868 grok.context(IApplicant) 864 869 grok.view(ApplicantManageFormPage) 865 grok.require('waeup. manageApplication')870 grok.require('waeup.viewApplication') 866 871 icon = 'actionicon_view.png' 867 872 text = 'View application record' -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicantdisplaypage.pt
r7200 r7240 11 11 </div> 12 12 13 <img src="" tal:attributes="src view/passport_url" /> 14 13 15 <table class="zebra"> 14 16 <tbody> 15 <tr>16 <td><img src="" tal:attributes="src view/passport_url" /></td>17 </tr>18 17 <tal:block repeat="widget view/widgets"> 19 18 <tr> … … 34 33 </td> 35 34 </tr> 35 <tr> 36 <td class="fieldname"> 37 Password: 38 </td> 39 <td> 40 <tal:password replace="view/hasPassword" /> 41 </td> 42 <tr> 36 43 </tbody> 37 44 </table> -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicanteditpage.pt
r7200 r7240 23 23 </div> 24 24 25 <table class=" form-fieldszebra">25 <table class="zebra"> 26 26 <tbody> 27 27 <tal:block repeat="widget view/widgets"> … … 62 62 </tr> 63 63 <tr tal:condition="view/manage_applications"> 64 <td class="label"><label>Password:</label></td> 65 <td> 66 <input name="password" type="password" /> 67 </td> 68 </tr> 69 <tr tal:condition="view/manage_applications"> 70 <td class="label"><label>Retype password:</label></td> 71 <td> 72 <input name="control_password" type="password" /> 73 </td> 74 </tr> 75 <tr tal:condition="view/manage_applications"> 64 76 <td class="label"><label>Application Transition:</label></td> 65 77 <td> -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/container.py
r7192 r7240 24 24 from waeup.sirp.applicants.interfaces import ( 25 25 IApplicantsContainer, IApplicantsContainerAdd, 26 IApplicantsContainerProvider, 26 IApplicantsContainerProvider, IApplicant 27 27 ) 28 28 from waeup.sirp.utils.helpers import attrs_to_fields … … 83 83 raise NotImplementedError() 84 84 85 def addApplicant(self, applicant): 86 """Add an applicant. 87 """ 88 if not IApplicant.providedBy(applicant): 89 raise TypeError( 90 'ApplicantsContainers contain only IApplicant instances') 91 self[applicant.application_number] = applicant 92 return 93 85 94 ApplicantsContainer = attrs_to_fields(ApplicantsContainer) 86 95 -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py
r7192 r7240 29 29 from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal 30 30 from zc.sourcefactory.basic import BasicSourceFactory 31 from waeup.sirp.interfaces import IWAeUPObject, year_range 31 from waeup.sirp.interfaces import IWAeUPObject, year_range, validate_email 32 32 from waeup.sirp.university.vocabularies import application_categories 33 33 from waeup.sirp.students.vocabularies import ( … … 42 42 MAX_UPLOAD_SIZE = 1024 * 20 43 43 44 # Define a valiation method for email addresses45 class NotAnEmailAddress(schema.ValidationError):46 __doc__ = u"Invalid email address"47 48 check_email = re.compile(49 r"[a-zA-Z0-9._%-]+@([a-zA-Z0-9-]+.)*[a-zA-Z]{2,4}").match50 def validate_email(value):51 if not check_email(value):52 raise NotAnEmailAddress(value)53 return True54 55 44 class ApplicantContainerProviderSource(BasicSourceFactory): 56 45 """A source offering all available applicants container types. … … 90 79 return "%s - %s" % ( 91 80 factory.container_title, factory.container_description) 92 93 class IResultEntry(IWAeUPObject):94 subject = schema.TextLine(95 title = u'Subject',96 description = u'The subject',97 required=False,98 )99 score = schema.TextLine(100 title = u'Score',101 description = u'The score',102 required=False,103 )104 81 105 82 class IApplicantsRoot(IWAeUPObject, IContainer): … … 263 240 """The data for an applicant. 264 241 265 This is a base interface with no field (except ``reg_no``)242 This is a base interface with no field 266 243 required. For use with importers, forms, etc., please use one of 267 244 the derived interfaces below, which set more fields to required … … 269 246 """ 270 247 history = Attribute('Object history, a list of messages.') 271 state = Attribute('Returns the application state of an applicant') 248 state = Attribute('The application state of an applicant') 249 fullname = Attribute('The fullname of an applicant') 272 250 application_date = Attribute('Date of submission, used for export only') 273 274 #def getApplicantsRootLogger(): 275 # """Returns the logger from the applicants root object 276 # """ 251 password = Attribute('Encrypted password of a applicant') 252 application_number = Attribute('The key under which the record is stored') 277 253 278 254 def loggerInfo(ob_class, comment): … … 280 256 """ 281 257 258 applicant_id = schema.TextLine( 259 title = u'Applicant Id', 260 required = False, 261 readonly = True, 262 ) 263 282 264 reg_no = schema.TextLine( 283 265 title = u'JAMB Registration Number', 284 266 readonly = True, 267 required = False, 285 268 ) 286 269 access_code = schema.TextLine( … … 329 312 email = schema.ASCIILine( 330 313 title = u'Email', 331 required = False,314 required = True, 332 315 constraint=validate_email, 333 316 ) … … 416 399 ) 417 400 418 class IApplicantPrincipalInfo(IPrincipalInfo):419 """Infos about principals that are applicants.420 """421 access_code = Attribute("The Access Code the user purchased")422 423 class IApplicantPrincipal(IPrincipal):424 """A principal that is an applicant.425 426 This interface extends zope.security.interfaces.IPrincipal and427 requires also an `id` and other attributes defined there.428 """429 access_code = schema.TextLine(430 title = u'Access Code',431 description = u'The access code purchased by the user.',432 required = True,433 readonly = True)434 435 class IApplicantsFormChallenger(Interface):436 """A challenger that uses a browser form to collect applicant437 credentials.438 """439 loginpagename = schema.TextLine(440 title = u'Loginpagename',441 description = u"""Name of the login form used by challenger.442 443 The form must provide an ``access_code`` input field.444 """)445 446 accesscode_field = schema.TextLine(447 title = u'Access code field',448 description = u'''Field of the login page which is looked up for449 access_code''',450 default = u'access_code',451 )452 453 454 class IApplicantSessionCredentials(Interface):455 """Interface for storing and accessing applicant credentials in a456 session.457 """458 459 def __init__(access_code):460 """Create applicant session credentials."""461 462 def getAccessCode():463 """Return the access code."""464 465 466 401 class IApplicantsContainerProvider(Interface): 467 402 """A provider for applicants containers. -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/permissions.py
r7192 r7240 29 29 grok.name('waeup.viewApplication') 30 30 31 class ViewApplicationsTab(grok.Permission): 32 grok.name('waeup.viewApplicationsTab') 33 34 class ViewMyApplicationDataTab(grok.Permission): 35 grok.name('waeup.viewMyApplicationDataTab') 36 31 37 class ManageApplication(grok.Permission): 32 38 grok.name('waeup.manageApplication') … … 36 42 grok.name('waeup.local.ApplicationOwner') 37 43 grok.title(u'Application Owner') 38 grok.permissions('waeup.handleApplication', 'waeup.viewApplication') 44 grok.permissions('waeup.handleApplication', 'waeup.viewApplication', 45 'waeup.Authenticated') 39 46 40 47 # Site role … … 42 49 class ApplicantRole(grok.Role): 43 50 grok.name('waeup.Applicant') 44 grok.permissions('waeup.viewAcademics' )51 grok.permissions('waeup.viewAcademics', 'waeup.viewMyApplicationDataTab') 45 52 46 53 class ApplicationsOfficer(grok.Role): 47 54 grok.name('waeup.ApplicationsOfficer') 48 55 grok.title(u'Applications Officer') 49 grok.permissions('waeup.manageApplication', 'waeup.viewApplication') 56 grok.permissions('waeup.manageApplication', 'waeup.viewApplication' 57 'waeup.viewApplicationsTab') -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py
r7193 r7240 32 32 from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser 33 33 from waeup.sirp.applicants import ( 34 ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac,34 Applicant, ApplicantFactory, ApplicantsContainer, 35 35 ApplicantImageStoreHandler, ApplicantImageNameChooser, 36 36 ) 37 from waeup.sirp.applicants.interfaces import I ResultEntry, IApplicant37 from waeup.sirp.applicants.interfaces import IApplicant 38 38 from waeup.sirp.testing import FunctionalTestCase, FunctionalLayer 39 39 … … 56 56 super(HelperTests, self).tearDown() 57 57 shutil.rmtree(self.workdir) 58 return59 60 def test_get_regno_or_ac(self):61 # we can get reg_no or access_code of an applicants if it is set62 appl1 = Applicant()63 appl2 = Applicant()64 appl2.reg_no = u'foo'65 appl3 = Applicant()66 appl3.access_code = u'bar'67 appl4 = Applicant()68 appl4.reg_no = u'foo'69 appl4.access_code = u'bar'70 self.assertTrue(71 get_regno_or_ac(appl1) is None)72 self.assertEqual(73 get_regno_or_ac(appl2), u'foo')74 self.assertEqual(75 get_regno_or_ac(appl3), u'bar')76 self.assertEqual(77 get_regno_or_ac(appl4), u'foo')78 58 return 79 59 … … 124 104 return 125 105 126 class ApplicantImageNameChooserTests(FunctionalTestCase):127 128 layer = FunctionalLayer129 130 def test_iface(self):131 # make sure we implement promised interfaces132 obj = ApplicantImageNameChooser(None) # needs a context normally133 verify.verifyClass(IFileStoreNameChooser, ApplicantImageNameChooser)134 verify.verifyObject(IFileStoreNameChooser, obj)135 return136 137 def test_name_chooser_available(self):138 # we can get a name chooser for applicant objects as adapter139 appl = Applicant()140 chooser = IFileStoreNameChooser(appl)141 self.assertTrue(chooser is not None)142 return143 144 def test_name_chooser_applicant_wo_container(self):145 # we can get an image filename for applicants not in a container146 appl = Applicant()147 appl.reg_no = u'MY_REG_NO'148 chooser = IFileStoreNameChooser(appl)149 result = chooser.chooseName()150 # the file would be stored in a ``_default`` directory.151 self.assertEqual(152 result, '__img-applicant___default/MY_REG_NO.jpg')153 return154 155 def test_name_chooser_applicant_w_container(self):156 appl = Applicant()157 appl.reg_no = u'MY_REG_NO'158 fake_container = grok.Container()159 fake_container.__name__ = 'folder'160 fake_container['appl'] = appl161 appl.__parent__ = fake_container162 chooser = IFileStoreNameChooser(appl)163 result = chooser.chooseName()164 self.assertEqual(165 result, '__img-applicant__folder/MY_REG_NO.jpg')166 return167 168 def test_name_chooser_check_name(self):169 # we can check file ids for applicants170 appl = Applicant()171 appl.reg_no = u'MY_REG_NO'172 fake_container = grok.Container()173 fake_container.__name__ = 'folder'174 fake_container['appl'] = appl175 appl.__parent__ = fake_container176 chooser = IFileStoreNameChooser(appl)177 result1 = chooser.checkName('foo')178 result2 = chooser.checkName('__img-applicant__folder/MY_REG_NO.jpg')179 self.assertEqual(result1, False)180 self.assertEqual(result2, True)181 return182 183 class ResultEntryTest(unittest.TestCase):184 185 def setUp(self):186 self.result_entry = ResultEntry()187 return188 189 def tearDown(self):190 pass191 192 def test_interfaces(self):193 verify.verifyClass(IResultEntry, ResultEntry)194 verify.verifyObject(IResultEntry, self.result_entry)195 196 def test_resultentry(self):197 entry = ResultEntry('Some subject', 3.7)198 assert entry.subject == 'Some subject'199 assert entry.score == 3.7200 201 106 class ApplicantTest(FunctionalTestCase): 202 107 … … 235 140 236 141 def test_factory(self): 237 obj = self.factory( )142 obj = self.factory(container=None) 238 143 assert isinstance(obj, Applicant) 239 144 -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py
r7238 r7240 16 16 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 17 ## 18 import shutil19 import tempfile20 18 import unittest 21 19 from zope.authentication.interfaces import IAuthentication 22 from zope.component import provideAdapter, getUtility, queryUtility 23 from zope.component.hooks import setSite 24 from zope.interface import verify 25 from zope.pluggableauth.interfaces import IAuthenticatedPrincipalFactory 26 from zope.publisher.browser import TestRequest 27 from zope.publisher.interfaces import IRequest 28 from zope.session.interfaces import ISession 29 from zope.testbrowser.testing import Browser 30 from zope.testing import cleanup 31 from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase 32 from waeup.sirp.app import University 33 from waeup.sirp.interfaces import IAuthPluginUtility 34 from waeup.sirp.applicants import ApplicantsContainer, Applicant 20 from zope.component import provideUtility, queryUtility, getGlobalSiteManager 21 from zope.interface.verify import verifyClass, verifyObject 22 from zope.password.password import SSHAPasswordManager 23 from zope.password.interfaces import IPasswordManager 24 from zope.pluggableauth import PluggableAuthentication 25 from zope.security.interfaces import Unauthorized 26 from zope.securitypolicy.role import Role 27 from zope.securitypolicy.interfaces import IRole, Allow 28 from waeup.sirp.authentication import get_principal_role_manager 29 from waeup.sirp.interfaces import IAuthPluginUtility, IUserAccount 35 30 from waeup.sirp.applicants.authentication import ( 36 ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin, 37 ApplicantCredentials, AuthenticatedApplicantPrincipalFactory, 38 ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthenticatorSetup) 31 ApplicantsAuthenticatorSetup, ApplicantAccount) 32 from waeup.sirp.applicants.tests.test_browser import ApplicantsFullSetup 33 from waeup.sirp.testing import FunctionalLayer 34 35 class ApplicantsAuthenticatorSetupTests(unittest.TestCase): 36 37 def test_iface(self): 38 obj = ApplicantsAuthenticatorSetup() 39 verifyClass(IAuthPluginUtility, ApplicantsAuthenticatorSetup) 40 verifyObject(IAuthPluginUtility, obj) 41 return 42 43 def test_register(self): 44 # Make sure registration works. 45 setup = ApplicantsAuthenticatorSetup() 46 pau = PluggableAuthentication() 47 setup.register(pau) 48 self.assertTrue('applicants' in pau.authenticatorPlugins) 49 return 50 51 def test_unregister(self): 52 # Make sure deregistration works. 53 setup = ApplicantsAuthenticatorSetup() 54 pau = PluggableAuthentication() 55 pau.authenticatorPlugins = ('applicants') 56 setup.unregister(pau) 57 self.assertTrue('applicants' not in pau.authenticatorPlugins) 58 return 39 59 40 60 41 class FakeBatch(dict): 42 def getAccessCode(self, id): 43 return self.get(id) 61 class FakeApplicant(object): 62 applicant_id = 'test_appl' 63 fullname = 'Tilman Gause' 64 password = None 65 email = None 66 phone = None 44 67 45 class FakeAccessCode(object):46 def __init__(self, repr, state = 'initialized'):47 self.representation = repr48 self.state = state49 68 50 class FakeApplication(object):51 def __init__(self, ac=None):52 self.access_code = ac69 class MinimalPAU(PluggableAuthentication): 70 def getPrincipal(self, id): 71 return 'faked principal' 53 72 54 def FakeSite(): 55 return { 56 'applicants': { 57 'APP': { 58 'APP-12345': FakeApplication(u'APP-12345'), 59 'APP-54321': FakeApplication(u'APP-54321'), 60 'APP-22222': FakeApplication(u'APP-OTHER'), 61 'APP-44444': FakeApplication(), 62 'APP-55555': FakeApplication(u'APP-OTHER'), 63 'APP-77777': FakeApplication(u'APP-77777'), 64 }, 65 }, 66 'accesscodes': { 67 'APP': FakeBatch({ 68 'APP-12345': FakeAccessCode('APP-12345'), 69 'APP-54321': FakeAccessCode('APP-54321', 'used'), 70 'APP-11111': FakeAccessCode('APP-11111'), 71 'APP-22222': FakeAccessCode('APP-22222'), 72 'APP-33333': FakeAccessCode('APP-33333', 'used'), 73 'APP-44444': FakeAccessCode('APP-44444', 'used'), 74 'APP-55555': FakeAccessCode('APP-55555', 'used'), 75 'APP-66666': FakeAccessCode('APP-66666', 'disabled'), 76 'APP-77777': FakeAccessCode('APP-77777', 'disabled'), 77 }) 78 } 79 } 80 81 class AuthenticatorPluginTest(FunctionalTestCase): 82 83 layer = FunctionalLayer 84 85 def create_applicant(self, cname, aname, ac=None): 86 # Create an applicant in an applicants container 87 setSite(self.app) 88 if not cname in self.app['applicants'].keys(): 89 container = ApplicantsContainer() 90 self.app['applicants'][cname] = container 91 applicant = Applicant() 92 if ac is not None: 93 applicant.access_code = ac 94 self.app['applicants'][cname][aname] = applicant 95 return 73 class ApplicantAccountTests(unittest.TestCase): 96 74 97 75 def setUp(self): 98 super(AuthenticatorPluginTest, self).setUp() 76 self.fake_stud = FakeApplicant() 77 self.account = ApplicantAccount(self.fake_stud) 99 78 100 # Setup a sample site for each test 101 app = University() 102 self.dc_root = tempfile.mkdtemp() 103 app['datacenter'].setStoragePath(self.dc_root) 79 # We provide a minimal PAU 80 pau = MinimalPAU() 81 provideUtility(pau, IAuthentication) 104 82 105 # Prepopulate the ZODB...106 self.getRootFolder()['app'] = app107 self.app = self.getRootFolder()['app']83 # We register a role 84 test_role = Role('waeup.test.Role', 'Testing Role') 85 provideUtility(test_role, IRole, name='waeup.test.Role') 108 86 109 fake_site = FakeSite() 110 for ckey, fake_container in fake_site['applicants'].items(): 111 for akey, fake_appl in fake_container.items(): 112 self.create_applicant(ckey, akey, fake_appl.access_code) 113 del self.app['accesscodes'] 114 self.app['accesscodes'] = fake_site['accesscodes'] 115 116 self.plugin = ApplicantsAuthenticatorPlugin() 87 # We have to setup a password manager utility manually as we 88 # have no functional test. In functional tests this would 89 # happen automatically, but it would take a lot more time to 90 # run the tests. 91 provideUtility( 92 SSHAPasswordManager(), IPasswordManager, 'SSHA') 117 93 return 118 94 119 95 def tearDown(self): 120 super(AuthenticatorPluginTest, self).tearDown() 121 shutil.rmtree(self.dc_root) 96 self.account.roles = [] # make sure roles are reset 97 gsm = getGlobalSiteManager() 98 to_clean = [] 99 # Clear up utilities registered in setUp 100 to_clean.append( 101 (IPasswordManager, queryUtility( 102 IPasswordManager, name='SSHA', default=None))) 103 to_clean.append( 104 (IAuthentication, queryUtility(IAuthentication, default=None))) 105 to_clean.append( 106 (IRole, queryUtility(IRole, name='test.Role', default=None))) 107 for iface, elem in to_clean: 108 if elem is not None: 109 gsm.unregisterUtility(elem, iface) 122 110 return 123 111 124 def test_invalid_credentials(self): 125 result = self.plugin.authenticateCredentials('not-a-dict') 126 assert result is None 127 128 result = self.plugin.authenticateCredentials( 129 dict(accesscode=None, foo='blah')) 130 assert result is None 131 132 result = self.plugin.authenticateCredentials( 133 dict(accesscode='Nonsense',)) 134 assert result is None 135 136 # Possible cases, where formal correct authentication 137 # data is not valid: 138 result = self.plugin.authenticateCredentials( 139 dict(accesscode='APP-33333')) 140 assert result is None 141 142 result = self.plugin.authenticateCredentials( 143 dict(accesscode='APP-55555')) 144 assert result is None 145 146 result = self.plugin.authenticateCredentials( 147 dict(accesscode='APP-66666')) 148 assert result is None 149 150 result = self.plugin.authenticateCredentials( 151 dict(accesscode='APP-77777')) 152 assert result is None 153 112 def test_iface(self): 113 verifyClass(IUserAccount, ApplicantAccount) 114 verifyObject(IUserAccount, self.account) 154 115 return 155 116 156 def test_valid_credentials(self): 157 """The six different cases where we allow login. 158 159 All other combinations should be forbidden. 160 """ 161 result = self.plugin.authenticateCredentials( 162 dict(accesscode='APP-11111')) 163 assert result is not None 164 165 result = self.plugin.authenticateCredentials( 166 dict(accesscode='APP-12345')) 167 assert result is not None 168 169 result = self.plugin.authenticateCredentials( 170 dict(accesscode='APP-54321')) 171 assert result is not None 172 173 # check the `principalInfo` method of authenticator 174 # plugin. This is only here to satisfy the coverage report. 175 assert self.plugin.principalInfo('not-an-id') is None 117 def test_set_password(self): 118 # make sure we can set a password. 119 self.account.setPassword('secret') 120 self.assertTrue(self.fake_stud.password is not None) 121 # we do not store plaintext passwords 122 self.assertTrue(self.fake_stud.password != 'secret') 123 # passwords are stored as unicode 124 self.assertTrue(isinstance(self.fake_stud.password, unicode)) 176 125 return 177 126 178 session_data = { 179 'zope.pluggableauth.browserplugins': {} 180 } 181 182 class FakeSession(dict): 183 def __init__(self, request): 184 pass 185 186 def get(self, key, default=None): 187 return self.__getitem__(key, default) 188 189 def __getitem__(self, key, default=None): 190 return session_data.get(key, default) 191 192 def __setitem__(self, key, value): 193 session_data[key] = value 127 def test_check_password(self): 128 # make sure we can check a password. 129 self.account.setPassword('secret') 130 result1 = self.account.checkPassword(None) 131 result2 = self.account.checkPassword('nonsense') 132 result3 = self.account.checkPassword('secret') 133 self.assertEqual(result1, False) 134 self.assertEqual(result2, False) 135 self.assertEqual(result3, True) 194 136 return 195 137 196 class CredentialsPluginTest(unittest.TestCase):197 198 def setUp(self):199 self.request = TestRequest()200 provideAdapter(FakeSession, (IRequest,), ISession)201 self.plugin = WAeUPApplicantCredentialsPlugin()202 self. full_request = TestRequest()203 se ssion_data['zope.pluggableauth.browserplugins'] = {}138 def test_check_unset_password(self): 139 # empty and unset passwords do not match anything 140 self.fake_stud.password = None 141 result1 = self.account.checkPassword('') 142 self.fake_stud.password = '' 143 result2 = self.account.checkPassword('') 144 self.assertEqual(result1, False) 145 self.assertEqual(result2, False) 204 146 return 205 147 206 def tearDown(self): 207 cleanup.tearDown() 148 def test_check_password_no_string(self): 149 # if passed in password is not a string, we gain no access 150 self.fake_stud.password = 'secret' 151 result1 = self.account.checkPassword(None) 152 result2 = self.account.checkPassword(object()) 153 self.assertEqual(result1, False) 154 self.assertEqual(result2, False) 208 155 return 209 156 210 def filled_request(self, form_dict): 211 request = TestRequest() 212 for key, value in form_dict.items(): 213 request.form[key] = value 214 return request 215 216 def test_extractCredentials_invalid(self): 217 result = self.plugin.extractCredentials('not-a-request') 218 assert result is None 157 def test_role_set(self): 158 # make sure we can set roles for principals denoted by account 159 prm = get_principal_role_manager() 160 self.assertEqual(prm.getPrincipalsAndRoles(), []) 161 self.account.roles = ['waeup.test.Role'] 162 self.assertEqual( 163 prm.getPrincipalsAndRoles(), 164 [('waeup.test.Role', 'test_appl', Allow)]) 219 165 return 220 166 221 def test_extractCredentials_empty(self): 222 result = self.plugin.extractCredentials(self.request) 223 assert result is None 167 def test_role_get(self): 168 # make sure we can get roles set for an account 169 self.assertEqual(self.account.roles, []) 170 self.account.roles = ['waeup.test.Role',] # set a role 171 self.assertEqual(self.account.roles, ['waeup.test.Role']) 224 172 return 225 173 226 def test_extractCredentials_full_set(self): 227 request = self.filled_request({ 228 'form.ac_prefix': 'APP', 229 'form.ac_series': '1', 230 'form.ac_number': '1234567890', 231 #'form.jamb_reg_no': 'JAMB_NUMBER', 232 }) 233 result = self.plugin.extractCredentials(request) 234 self.assertEqual(result, {'accesscode': 'APP-1-1234567890'}) 235 return 236 237 def test_extractCredentials_accesscode_only(self): 238 request = self.filled_request({ 239 'form.ac_prefix': 'APP', 240 'form.ac_series': '1', 241 'form.ac_number': '1234567890', 242 }) 243 result = self.plugin.extractCredentials(request) 244 self.assertEqual(result, {'accesscode': 'APP-1-1234567890'}) 245 return 246 247 def test_extractCredentials_from_empty_session(self): 248 session_data['zope.pluggableauth.browserplugins']['credentials'] = None 249 result = self.plugin.extractCredentials(self.request) 250 assert result is None 251 return 252 253 def test_extractCredentials_from_nonempty_session(self): 254 credentials = ApplicantCredentials('APP-1-12345') 255 session_data['zope.pluggableauth.browserplugins'][ 256 'credentials'] = credentials 257 result = self.plugin.extractCredentials(self.request) 258 self.assertEqual(result, {'accesscode': 'APP-1-12345'}) 259 return 260 261 262 class ApplicantCredentialsTest(unittest.TestCase): 263 264 def setUp(self): 265 self.credentials = ApplicantCredentials('SOME_ACCESSCODE') 266 return 267 268 def tearDown(self): 269 return 270 271 def test_methods(self): 272 self.assertEqual(self.credentials.getAccessCode(), 'SOME_ACCESSCODE') 273 assert self.credentials.getLogin() is None 274 assert self.credentials.getPassword() is None 275 return 276 277 class FakePluggableAuth(object): 278 prefix = 'foo' 279 280 class PrincipalFactoryTest(unittest.TestCase): 281 282 def setUp(self): 283 self.info = ApplicantPrincipalInfo('APP-1-1234567890') 284 return 285 286 def tearDown(self): 287 pass 288 289 def test_principalFactory_interface(self): 290 verify.verifyClass(IAuthenticatedPrincipalFactory, 291 AuthenticatedApplicantPrincipalFactory 292 ) 293 return 294 295 def test_principalFactory_create(self): 296 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 297 298 assert factory.info is self.info 299 assert factory.request is None 300 return 301 302 def test_principalFactory_call_w_prefix(self): 303 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 304 principal = factory(FakePluggableAuth()) 305 306 assert isinstance(principal, ApplicantPrincipal) 307 self.assertEqual(principal.__repr__(), 308 "ApplicantPrincipal('foo.APP-1-1234567890')") 309 self.assertEqual(principal.id, 'foo.APP-1-1234567890') 310 return 311 312 def test_principalFactory_call_wo_prefix(self): 313 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 314 fake_auth = FakePluggableAuth() 315 fake_auth.prefix = None 316 principal = factory(fake_auth) 317 self.assertEqual(principal.id, 'APP-1-1234567890') 318 return 319 320 class PAUSetupTest(FunctionalTestCase): 321 # Check correct setup of authentication components in the 322 # applicants subpackage. 323 324 # When a university is created, we want by default have our local 325 # authentication components (an authenticator plugin and a 326 # credentials plugin) to be registered with the local PAU. Admins 327 # can remove these components on the fly later-on if they wish. 174 class FunctionalApplicantAuthTests(ApplicantsFullSetup): 328 175 329 176 layer = FunctionalLayer 330 177 331 178 def setUp(self): 332 super(PAUSetupTest, self).setUp() 333 334 # Setup a sample site for each test 335 app = University() 336 self.dc_root = tempfile.mkdtemp() 337 app['datacenter'].setStoragePath(self.dc_root) 338 339 # Prepopulate the ZODB... 340 self.getRootFolder()['app'] = app 341 self.app = self.getRootFolder()['app'] 342 self.browser = Browser() 343 self.browser.handleErrors = False 179 super(FunctionalApplicantAuthTests, self).setUp() 180 return 344 181 345 182 def tearDown(self): 346 super(PAUSetupTest, self).tearDown() 347 shutil.rmtree(self.dc_root) 348 349 def test_extra_auth_plugins_installed(self): 350 # Check whether the auth plugins defined in here are setup 351 # automatically when a university is created 352 353 # Get the PAU responsible for the local site ('app') 354 pau = getUtility(IAuthentication, context=self.app) 355 cred_plugins = pau.getCredentialsPlugins() 356 auth_plugins = pau.getAuthenticatorPlugins() 357 cred_names = [name for name, plugin in cred_plugins] 358 auth_names = [name for name, plugin in auth_plugins] 359 360 # Make sure our local ApplicantsAuthenticatorPlugin is registered... 361 self.assertTrue('applicants' in auth_names) 362 # Make sure our local WAeUPApplicantCredentialsPlugin is registered... 363 self.assertTrue('applicant_credentials' in cred_names) 183 super(FunctionalApplicantAuthTests, self).tearDown() 364 184 return 365 366 class FakePAU(object):367 credentialsPlugins = ()368 authenticatorPlugins = ()369 370 class ApplicantsAuthenticatorSetupTests(FunctionalTestCase):371 372 layer = FunctionalLayer373 374 def test_ifaces(self):375 # make sure we fullfill the interface promises376 obj = ApplicantsAuthenticatorSetup()377 verify.verifyClass(IAuthPluginUtility, ApplicantsAuthenticatorSetup)378 verify.verifyObject(IAuthPluginUtility, obj)379 return380 381 def test_utility_available(self):382 # we can get an applicant auth utility by lookup383 util = queryUtility(IAuthPluginUtility,384 name='applicants_auth_setup')385 self.assertTrue(util is not None)386 return387 388 def test_register(self):389 # make sure we can register additional components390 pau = FakePAU()391 result = ApplicantsAuthenticatorSetup().register(pau)392 self.assertEqual(393 result.credentialsPlugins, ('applicant_credentials',))394 self.assertEqual(395 result.authenticatorPlugins, ('applicants',))396 return397 398 def test_unregister(self):399 # make sure we can unregister applicant auth components400 pau = FakePAU()401 util = ApplicantsAuthenticatorSetup()402 pau = util.register(pau)403 result = util.unregister(pau)404 self.assertEqual(405 result.credentialsPlugins, ())406 self.assertEqual(407 result.authenticatorPlugins, ())408 return -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py
r7226 r7240 33 33 from waeup.sirp.applicants.container import ApplicantsContainer 34 34 from waeup.sirp.applicants.applicant import Applicant 35 from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser 35 from waeup.sirp.interfaces import ( 36 IExtFileStore, IFileStoreNameChooser, IUserAccount) 36 37 from waeup.sirp.university.faculty import Faculty 37 38 from waeup.sirp.university.department import Department … … 68 69 setSite(app) 69 70 71 self.login_path = 'http://localhost/app/login' 70 72 self.root_path = 'http://localhost/app/applicants' 71 73 self.manage_root_path = self.root_path + '/@@manage' … … 77 79 applicantscontainer = ApplicantsContainer() 78 80 applicantscontainer.ac_prefix = 'APP' 81 applicantscontainer.code = u'app2009' 79 82 applicantscontainer.prefix = 'app' 80 83 applicantscontainer.year = 2009 … … 84 87 applicantscontainer.enddate = date.today() + delta 85 88 self.app['applicants']['app2009'] = applicantscontainer 89 self.applicantscontainer = self.app['applicants']['app2009'] 86 90 87 91 # Populate university … … 112 116 113 117 # Add an applicant 114 self.applicant = Applicant() 115 self.pin_applicant = unicode(self.pins[1]) 116 self.applicant.access_code = self.pin_applicant 117 app['applicants']['app2009'][self.pin_applicant] = self.applicant 118 self.applicant = Applicant(container=applicantscontainer) 119 app['applicants']['app2009'][ 120 self.applicant.application_number] = self.applicant 121 IUserAccount( 122 self.app['applicants']['app2009'][ 123 self.applicant.application_number]).setPassword('apwd') 124 self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % ( 125 'app2009', self.applicant.application_number, 'manage') 126 self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % ( 127 'app2009', self.applicant.application_number, 'edit') 128 self.view_path = 'http://localhost/app/applicants/%s/%s' % ( 129 'app2009', self.applicant.application_number) 130 131 def login(self): 132 # Perform an applicant login. This creates an applicant record. 133 # 134 # This helper also sets `self.applicant`, which is the 135 # applicant object created. 136 self.browser.open(self.login_path) 137 self.browser.getControl(name="form.login").value = self.applicant.applicant_id 138 self.browser.getControl(name="form.password").value = 'apwd' 139 self.browser.getControl("Login").click() 140 141 def fill_correct_values(self): 142 # Fill the edit form with suitable values 143 self.browser.getControl(name="form.firstname").value = 'John' 144 self.browser.getControl(name="form.lastname").value = 'Tester' 145 self.browser.getControl(name="form.course1").value = ['CERT1'] 146 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 147 self.browser.getControl(name="form.lga").value = ['foreigner'] 148 self.browser.getControl(name="form.sex").value = ['m'] 149 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 118 150 119 151 def tearDown(self): … … 253 285 self.assertEqual(self.browser.headers['Status'], '200 Ok') 254 286 self.assertEqual(self.browser.url, self.add_applicant_path) 255 self.browser.getControl(name="form.ac_series").value = self.existing_series 256 self.browser.getControl(name="form.ac_number").value = self.existing_number 287 self.browser.getControl(name="form.firstname").value = 'Albert' 288 self.browser.getControl(name="form.lastname").value = 'Einstein' 289 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 257 290 self.browser.getControl("Create application record").click() 258 291 self.assertTrue('Application initialized' in self.browser.contents) 259 self.browser.open(self.add_applicant_path)260 self.browser.getControl(name="form.ac_series").value = '123'261 self.browser.getControl(name="form.ac_number").value = '456'262 self.browser.getControl("Create application record").click()263 self.assertTrue('is not a valid access code' in self.browser.contents)264 292 self.browser.open(self.container_manage_path) 265 293 self.assertEqual(self.browser.headers['Status'], '200 Ok') 266 294 ctrl = self.browser.getControl(name='val_id') 267 ctrl.getControl(value=self.existing_pin).selected = True 295 value = ctrl.options[0] 296 ctrl.getControl(value=value).selected = True 268 297 self.browser.getControl("Remove selected", index=0).click() 269 298 self.assertTrue('Successfully removed:' in self.browser.contents) 270 299 self.browser.open(self.add_applicant_path) 271 existing_pin = self.pins[2] 272 parts = existing_pin.split('-')[1:] 273 existing_series, existing_number = parts 274 self.browser.getControl(name="form.ac_series").value = existing_series 275 self.browser.getControl(name="form.ac_number").value = existing_number 300 self.browser.getControl(name="form.firstname").value = 'Albert' 301 self.browser.getControl(name="form.lastname").value = 'Einstein' 302 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 276 303 self.browser.getControl("Create application record").click() 277 304 self.assertTrue('Application initialized' in self.browser.contents) 278 self.browser.open(self.container_manage_path)279 self.assertTrue(existing_pin in self.browser.contents)280 del self.app['applicants']['app2009'][existing_pin]281 ctrl = self.browser.getControl(name='val_id')282 ctrl.getControl(value=existing_pin).selected = True283 self.browser.getControl("Remove selected", index=0).click()284 self.assertMatches('...Could not delete...', self.browser.contents)285 305 return 286 306 … … 288 308 # Managers can manage applicants 289 309 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 290 self.applicant_path = 'http://localhost/app/applicants/app2009/%s' % self.pin_applicant 291 self.applicant_view_path = self.applicant_path + '/index' 292 self.applicant_manage_path = self.applicant_path + '/manage' 293 self.applicant_slip_path = self.applicant_path + '/application_slip.pdf' 294 self.browser.open(self.applicant_manage_path) 295 self.assertEqual(self.browser.headers['Status'], '200 Ok') 296 self.browser.getControl(name="form.email").value = 'abc' 297 self.browser.getControl("Save").click() 298 self.assertMatches('...Invalid email address...', self.browser.contents) 299 self.browser.getControl(name="form.email").value = 'abc@def.gh' 300 self.browser.getControl(name="form.firstname").value = 'John' 301 self.browser.getControl(name="form.lastname").value = 'Tester' 302 self.browser.getControl(name="form.course1").value = ['CERT1'] 303 self.browser.getControl(name="form.course_admitted").value = ['CERT1'] 304 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 305 self.browser.getControl(name="form.lga").value = ['foreigner'] 306 self.browser.getControl(name="form.sex").value = ['m'] 310 self.slip_path = self.view_path + '/application_slip.pdf' 311 self.browser.open(self.manage_path) 312 self.assertEqual(self.browser.headers['Status'], '200 Ok') 313 self.fill_correct_values() 307 314 self.browser.getControl(name="transition").value = ['start'] 308 315 self.browser.getControl("Save").click() 309 316 self.assertMatches('...Form has been saved...', self.browser.contents) 310 317 self.assertMatches('...Application started by zope.mgr...', self.browser.contents) 311 self.browser.open(self. applicant_view_path)312 self.assertEqual(self.browser.headers['Status'], '200 Ok') 313 self.browser.open(self. applicant_slip_path)318 self.browser.open(self.view_path) 319 self.assertEqual(self.browser.headers['Status'], '200 Ok') 320 self.browser.open(self.slip_path) 314 321 self.assertEqual(self.browser.headers['Status'], '200 Ok') 315 322 self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') 316 self.browser.open(self. applicant_manage_path)323 self.browser.open(self.manage_path) 317 324 self.browser.getControl(name="form.course_admitted").value = [] 318 325 self.browser.getControl("Save").click() 319 self.browser.open(self. applicant_slip_path)326 self.browser.open(self.slip_path) 320 327 self.assertEqual(self.browser.headers['Status'], '200 Ok') 321 328 self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') 322 return323 324 def test_view_applicant(self):325 # Applicants can login and view their application326 self.login_path = 'http://localhost/app/applicants/app2009/login'327 self.browser.open(self.login_path)328 pin = self.pins[2]329 parts = pin.split('-')[1:]330 existing_series, existing_number = parts331 ac_series = self.browser.getControl(name="form.ac_series")332 ac_series.value = existing_series333 ac_number = self.browser.getControl(name="form.ac_number")334 ac_number.value = existing_number335 self.browser.getControl(name="SUBMIT").click()336 self.assertTrue(self.browser.url != self.login_path)337 self.assertEqual(self.browser.headers['Status'], '200 Ok')338 329 return 339 330 340 331 def test_passport_edit_view(self): 341 332 # We get a default image after login 342 login_path = 'http://localhost/app/applicants/app2009/login' 343 self.browser.open(login_path) 344 pin = self.pins[2] 345 parts = pin.split('-')[1:] 346 existing_series, existing_number = parts 347 ac_series = self.browser.getControl(name="form.ac_series") 348 ac_series.value = existing_series 349 ac_number = self.browser.getControl(name="form.ac_number") 350 ac_number.value = existing_number 351 self.browser.getControl(name="SUBMIT").click() 352 pin = self.pins[2] 353 #appl = self.getRootFolder()['app']['applicants']['app2009'] 354 #appl = appl[pin] 355 #passp = appl.passport 356 #passp_len = len(passp.file.read()) 357 #self.assertEqual(passp_len, PH_LEN) 358 #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg') 359 image_url = "%s/%s" % (self.browser.url, 'passport.jpg') 360 #self.browser.open(image_url) 361 self.browser.open('passport.jpg') 333 self.browser.open(self.login_path) 334 self.login() 335 self.browser.open(self.browser.url + '/passport.jpg') 362 336 self.assertEqual(self.browser.headers['status'], '200 Ok') 363 337 self.assertEqual(self.browser.headers['content-type'], 'image/jpeg') … … 366 340 self.browser.headers['content-length'], str(PH_LEN)) 367 341 368 369 342 def test_edit_applicant(self): 370 343 # Applicants can edit their record 371 self.login_path = 'http://localhost/app/applicants/app2009/login'372 344 self.browser.open(self.login_path) 373 pin = self.pins[2] 374 parts = pin.split('-')[1:] 375 existing_series, existing_number = parts 376 ac_series = self.browser.getControl(name="form.ac_series") 377 ac_series.value = existing_series 378 ac_number = self.browser.getControl(name="form.ac_number") 379 ac_number.value = existing_number 380 self.browser.getControl(name="SUBMIT").click() 345 self.login() 346 self.browser.open(self.edit_path) 381 347 self.assertTrue(self.browser.url != self.login_path) 382 348 self.assertEqual(self.browser.headers['Status'], '200 Ok') 383 self.browser.getControl(name="form.firstname").value = 'John' 384 self.browser.getControl(name="form.lastname").value = 'Tester' 385 self.browser.getControl(name="form.course1").value = ['CERT1'] 386 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 387 self.browser.getControl(name="form.lga").value = ['foreigner'] 388 self.browser.getControl(name="form.sex").value = ['m'] 349 self.fill_correct_values() 389 350 self.browser.getControl("Save").click() 390 351 self.assertMatches('...Form has been saved...', self.browser.contents) … … 421 382 return 422 383 423 # We have no local roles yet424 #def test_local_roles_add_delete(self):425 # # Managers can assign and delete local roles of applicants containers426 # myusers = self.app['users']427 # myusers.addUser('bob', 'bobssecret')428 # self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')429 # self.browser.open(self.manage_container_path)430 # self.browser.getControl(name="user").value = ['bob']431 # self.browser.getControl(name="local_role").value = [432 # 'waeup.local.ApplicationsOfficer']433 # self.browser.getControl("Add local role").click()434 # self.assertTrue('<td>bob</td>' in self.browser.contents)435 # ctrl = self.browser.getControl(name='role_id')436 # ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True437 # self.browser.getControl("Remove selected local roles").click()438 # self.assertTrue('Successfully removed:' in self.browser.contents)439 # self.assertFalse('<td>bob</td>' in self.browser.contents)440 # return441 442 class LoginTest(FunctionalTestCase):443 # Here we check login view of applicants containers.444 #445 # Tests in here do only cover login attempts without any PINs446 # created before.447 448 layer = FunctionalLayer449 450 def setUp(self):451 super(LoginTest, self).setUp()452 453 # Setup a sample site for each test454 app = University()455 self.dc_root = tempfile.mkdtemp()456 app['datacenter'].setStoragePath(self.dc_root)457 self.login_path = 'http://localhost/app/applicants/testapplicants/login'458 459 # Add an applicants container where we can login (or not)460 applicantscontainer = ApplicantsContainer()461 applicantscontainer.ac_prefix = 'APP'462 delta = timedelta(days=10)463 applicantscontainer.startdate = date.today() - delta464 applicantscontainer.enddate = date.today() + delta465 app['applicants']['testapplicants'] = applicantscontainer466 467 # Put the prepopulated site into test ZODB and prepare test468 # browser469 self.getRootFolder()['app'] = app470 self.browser = Browser()471 self.browser.handleErrors = False472 473 def tearDown(self):474 super(LoginTest, self).tearDown()475 shutil.rmtree(self.dc_root)476 477 def test_anonymous_access(self):478 # Anonymous users can access a login page479 self.browser.open(self.login_path)480 self.assertEqual(self.browser.headers['Status'], '200 Ok')481 return482 483 def test_anonymous_invalid_creds(self):484 # Anonymous users giving invalid credentials stay at the page485 self.browser.open(self.login_path)486 # We do not give credentials but send the form as-is487 submit = self.browser.getControl(name='SUBMIT')488 submit.click()489 # We are still at the same page...490 self.assertEqual(self.browser.url, self.login_path)491 self.assertEqual(self.browser.headers['Status'], '200 Ok')492 return493 494 def test_anonymous_invalid_creds_warning(self):495 # Entering wrong credentials will yield a warning496 self.browser.open(self.login_path)497 # We do not give credentials but send the form as-is498 submit = self.browser.getControl(name='SUBMIT')499 submit.click()500 self.assertTrue(501 'Entered credentials are invalid' in self.browser.contents)502 return503 504 def test_manager_no_warnings(self):505 # Browsing the login screen as a manager, won't raise warnings506 # Authenticate ourself as manager507 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')508 self.browser.open(self.login_path)509 # Submit the form w/o any credentials510 self.browser.getControl(name="SUBMIT").click()511 self.assertTrue(512 'Entered credentials are invalid' not in self.browser.contents)513 return514 515 def test_manager_no_redirect(self):516 # Browsing the login screen as a manager won't trigger a redirect517 # Authenticate ourself as manager518 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')519 self.browser.open(self.login_path)520 # Submit the form w/o any credentials521 self.browser.getControl(name="SUBMIT").click()522 self.assertEqual(self.browser.url, self.login_path)523 return524 525 def test_display_entered_values(self):526 # After submit the entered values are displayed in the form527 self.browser.open(self.login_path)528 # Enter some value we can look for after submit529 ac_series = self.browser.getControl(name="form.ac_series")530 ac_series.value = '666'531 self.browser.getControl(name="SUBMIT").click()532 self.assertTrue('666' in self.browser.contents)533 return534 535 class LoginTestWithPINs(LoginTest):536 # Here we check login view of applicants containers with PINs provided.537 538 # As setting up pins is time-consuming we only set them up when539 # really needed (i.e. in this separate TestCase).540 541 layer = FunctionalLayer542 543 def setUp(self):544 super(LoginTestWithPINs, self).setUp()545 546 # Create 5 access codes with prefix'FOO' and cost 9.99 each547 pin_container = self.getRootFolder()['app']['accesscodes']548 pin_container.createBatch(549 datetime.now(), 'some_userid', 'APP', 9.99, 5)550 pins = pin_container[pin_container.keys()[0]].values()551 self.pins = [x.representation for x in pins]552 self.existing_pin = self.pins[0]553 parts = self.existing_pin.split('-')[1:]554 self.existing_series, self.existing_number = parts555 self.browser.handleErrors = False556 557 def tearDown(self):558 super(LoginTestWithPINs, self).tearDown()559 560 def test_anonymous_valid_login(self):561 # If we enter valid credentials, we get to the applicants form562 self.browser.open(self.login_path)563 # Enter some value we can look for after submit564 ac_series = self.browser.getControl(name="form.ac_series")565 ac_series.value = self.existing_series566 ac_number = self.browser.getControl(name="form.ac_number")567 ac_number.value = self.existing_number568 self.browser.getControl(name="SUBMIT").click()569 # We should be redirected to applicants form.570 self.assertTrue(self.browser.url != self.login_path)571 # Applicants see their Access Code in the contact form572 #self.browser.getLink("Contact").click()573 #self.assertTrue(574 # 'Access Code:' in self.browser.contents)575 return576 577 def test_anonymous_invalid_login(self):578 # If we enter wrong credentials we won't get far579 self.browser.open(self.login_path)580 # Enter some value we can look for after submit581 ac_series = self.browser.getControl(name="form.ac_series")582 ac_series.value = 'illegal series'583 ac_number = self.browser.getControl(name="form.ac_number")584 ac_number.value = 'invalid number'585 self.browser.getControl(name="SUBMIT").click()586 # We get a warning message587 self.assertTrue(588 'Entered credentials are invalid' in self.browser.contents)589 # We stay at the login page (no redirect)590 self.assertTrue(self.browser.url == self.login_path)591 return592 593 384 class ApplicantsPassportTests(ApplicantsFullSetup): 594 385 # Tests for uploading/browsing the passport image of appplicants 595 386 596 387 layer = FunctionalLayer 597 598 def setUp(self):599 super(ApplicantsPassportTests, self).setUp()600 self.login_path = 'http://localhost/app/applicants/app2009/login'601 self.pin = self.pins[2]602 self.existing_series, self.existing_number = self.pin.split('-')[1:]603 self.edit_path = 'http://localhost/app/applicants/app2009/%s/edit' % (604 self.pin)605 self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (606 'app2009', self.pin, 'manage')607 608 def tearDown(self):609 super(ApplicantsPassportTests, self).tearDown()610 611 def login(self):612 # Perform an applicant login. This creates an applicant record.613 #614 # This helper also sets `self.applicant`, which is the615 # applicant object created.616 self.browser.open(self.login_path)617 ac_series = self.browser.getControl(name="form.ac_series")618 ac_series.value = self.existing_series619 ac_number = self.browser.getControl(name="form.ac_number")620 ac_number.value = self.existing_number621 self.browser.getControl(name="SUBMIT").click()622 self.applicant = self.app['applicants']['app2009'][self.pin]623 624 def fill_correct_values(self):625 # Fill the edit form with suitable values626 self.browser.getControl(name="form.firstname").value = 'John'627 self.browser.getControl(name="form.lastname").value = 'Tester'628 self.browser.getControl(name="form.course1").value = ['CERT1']629 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'630 self.browser.getControl(name="form.lga").value = ['foreigner']631 self.browser.getControl(name="form.sex").value = ['m']632 388 633 389 def image_url(self, filename): … … 638 394 #import pdb; pdb.set_trace() 639 395 self.login() 640 self.assertEqual(self.browser.url, self.edit_path) 396 self.assertEqual(self.browser.url, self.view_path) 397 self.browser.open(self.edit_path) 398 # There is a correct <img> link included 399 self.assertTrue( 400 '<img src="passport.jpg" />' in self.browser.contents) 401 # Browsing the link shows a real image 402 self.browser.open(self.image_url('passport.jpg')) 403 self.assertEqual( 404 self.browser.headers['content-type'], 'image/jpeg') 405 self.assertEqual(len(self.browser.contents), PH_LEN) 406 407 def test_after_submit_default_browsable(self): 408 # After submitting an applicant form the default image is 409 # still visible 410 self.login() 411 self.browser.open(self.edit_path) 412 self.browser.getControl("Save").click() # submit form 641 413 # There is a correct <img> link included 642 414 self.assertTrue( … … 648 420 self.assertEqual(len(self.browser.contents), PH_LEN) 649 421 650 def test_after_submit_default_browsable(self):651 # After submitting an applicant form the default image is652 # still visible653 self.login()654 self.browser.getControl("Save").click() # submit form655 # There is a correct <img> link included656 self.assertTrue(657 '<img src="passport.jpg" />' in self.browser.contents)658 # Browsing the link shows a real image659 self.browser.open(self.image_url('passport.jpg'))660 self.assertEqual(661 self.browser.headers['content-type'], 'image/jpeg')662 self.assertEqual(len(self.browser.contents), PH_LEN)663 664 422 def test_uploaded_image_respects_file_size_restriction(self): 665 423 # When we upload an image that is too big ( > 10 KB) we will 666 424 # get an error message 667 425 self.login() 426 self.browser.open(self.edit_path) 668 427 # Create a pseudo image file and select it to be uploaded in form 669 428 photo_content = 'A' * 1024 * 21 # A string of 21 KB size … … 694 453 # even if there are still errors in the form 695 454 self.login() 455 self.browser.open(self.edit_path) 696 456 # Create a pseudo image file and select it to be uploaded in form 697 457 photo_content = 'I pretend to be a graphics file' … … 714 474 # stored in an imagestorage 715 475 self.login() 476 self.browser.open(self.edit_path) 716 477 # Create a pseudo image file and select it to be uploaded in form 717 478 pseudo_image = StringIO('I pretend to be a graphics file') … … 731 492 # if there are no errors in form 732 493 self.login() 494 self.browser.open(self.edit_path) 733 495 self.fill_correct_values() # fill other fields with correct values 734 496 # Create a pseudo image file and select it to be uploaded in form … … 751 513 # stored in an imagestorage if form contains no errors 752 514 self.login() 515 self.browser.open(self.edit_path) 753 516 self.fill_correct_values() # fill other fields with correct values 754 517 # Create a pseudo image file and select it to be uploaded in form … … 768 531 # Make sure uploaded images do really differ if we eject a 769 532 # change notfication (and do not if we don't) 770 self.login() # Create applicant form 533 self.login() 534 self.browser.open(self.edit_path) 771 535 self.fill_correct_values() # fill other fields with correct values 772 536 self.browser.getControl("Save").click() # submit form … … 795 559 # Make sure that a correctly filled form with passport picture 796 560 # can be submitted 797 self.login() # Create applicant form 561 self.login() 562 self.browser.getLink("Edit application record").click() 798 563 self.fill_correct_values() # fill other fields with correct values 799 564 # Create a pseudo image file and select it to be uploaded in form … … 814 579 # Now do the whole thing again but with correct state 815 580 self.login() 581 self.browser.open(self.edit_path) 816 582 self.fill_correct_values() 817 583 pseudo_image = StringIO('I pretend to be a graphics file') … … 836 602 def test_locking(self): 837 603 # Make sure that locked forms can't be submitted 838 self.login() # Create applicant form 604 self.login() 605 self.browser.open(self.edit_path) 839 606 self.fill_correct_values() # fill other fields with correct values 840 607 # Create a pseudo image file and select it to be uploaded in form … … 844 611 file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') 845 612 self.browser.getControl("Save").click() 846 self.browser.getLink("Logout").click() 847 848 # Login as manager and lock the form 849 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 850 self.browser.open(self.manage_path) 851 self.browser.getControl(name="form.locked").value = True 852 self.browser.getControl("Save").click() 853 self.browser.getLink("Logout").click() 854 855 # Login as applicant again and try to open the edit form 856 self.login() 613 # Now we lock the form 614 self.applicant.locked = True 857 615 self.browser.open(self.edit_path) 858 616 self.assertEqual(self.browser.headers['Status'], '200 Ok') -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_catalog.py
r7193 r7240 58 58 # Create an applicant in an applicants container 59 59 self.container = ApplicantsContainer() 60 self.container.code = u"mystuff" 60 61 setSite(self.app) 61 62 self.app['applicants']['mystuff'] = self.container 62 self.applicant = Applicant() 63 self.ac = u'FOO-666-123456789' 64 self.applicant.access_code = self.ac 65 self.app['applicants']['mystuff'][self.ac] = self.applicant 63 self.applicant = Applicant(container=self.container) 64 self.app['applicants']['mystuff'][ 65 self.applicant.application_number] = self.applicant 66 66 return 67 67 … … 80 80 self.create_applicant() 81 81 q = getUtility(IQuery) 82 subquery = Eq(('applicants_catalog', 'access_code'), self.ac) 82 subquery = Eq(('applicants_catalog', 'applicant_id'), 83 self.applicant.applicant_id) 83 84 results = list(q.searchResults(subquery)) 84 85 self.assertEqual(len(results), 1) 85 86 86 result_applicant = results[0] 87 87 self.assertTrue(isinstance(result_applicant, Applicant)) 88 self.assertEqual(result_applicant.a ccess_code, self.ac)88 self.assertEqual(result_applicant.applicant_id, self.applicant.applicant_id) -
main/waeup.sirp/trunk/src/waeup/sirp/authentication.py
r7239 r7240 76 76 77 77 A SIRP principal info is created with id, login, title, description, 78 phone and email.78 phone, email and user_type. 79 79 """ 80 80 grok.implements(ISIRPPrincipalInfo) 81 81 82 def __init__(self, id, title, description, email, phone ):82 def __init__(self, id, title, description, email, phone, user_type): 83 83 self.id = id 84 84 self.title = title … … 86 86 self.email = email 87 87 self.phone = phone 88 self.user_type = user_type 88 89 self.credentialsPlugin = None 89 90 self.authenticatorPlugin = None … … 92 93 """A portal principal. 93 94 94 SIRP principals provide an extra `email` and `phone`95 SIRP principals provide an extra `email`, `phone` and `user_type` 95 96 attribute extending ordinary principals. 96 97 """ … … 99 100 100 101 def __init__(self, id, title=u'', description=u'', email=u'', 101 phone=None, prefix=None):102 phone=None, user_type=u'', prefix=None): 102 103 self.id = id 103 104 if prefix is not None: … … 108 109 self.email = email 109 110 self.phone = phone 111 self.user_type = user_type 110 112 111 113 def __repr__(self): … … 134 136 self.info.email, 135 137 self.info.phone, 138 self.info.user_type, 136 139 authentication.prefix, 137 140 ) … … 147 150 148 151 def __init__(self, name, password, title=None, description=None, 149 email=None, phone=None, roles = []):152 email=None, phone=None, user_type=None, roles = []): 150 153 self.name = name 151 154 if title is None: … … 157 160 self.email = email 158 161 self.phone = phone 162 self.user_type = user_type 159 163 self.setPassword(password) 160 164 #self.setSiteRolesForPrincipal(roles) … … 211 215 if not isinstance(credentials, dict): 212 216 return None 217 213 218 if not ('login' in credentials and 'password' in credentials): 214 219 return None 215 220 account = self.getAccount(credentials['login']) 216 217 221 if account is None: 218 222 return None … … 223 227 description=account.description, 224 228 email=account.email, 225 phone=account.phone) 229 phone=account.phone, 230 user_type=u'user') 226 231 227 232 def principalInfo(self, id): … … 233 238 description=account.description, 234 239 email=account.email, 235 phone=account.phone) 240 phone=account.phone, 241 user_type=u'user') 236 242 237 243 def getAccount(self, login): -
main/waeup.sirp/trunk/src/waeup/sirp/browser/layout.py
r7195 r7240 32 32 from waeup.sirp.browser.theming import get_all_themes, WAeUPThemeGray1 33 33 from waeup.sirp.students.interfaces import IStudentNavigation 34 from waeup.sirp.applicants.interfaces import IApplicant 34 35 from waeup.sirp.authentication import get_principal_role_manager 35 36 … … 163 164 if usertitle == 'Unauthenticated User': 164 165 return u'Anonymous User' 165 elif usertitle == 'Applicant':166 return self.request.principal.id167 166 return usertitle 168 167 … … 172 171 userid = self.request.principal.id 173 172 return userid 173 174 def isStudent(self): 175 usertype = getattr(self.request.principal, 'user_type', None) 176 if not usertype: 177 return False 178 return self.request.principal.user_type == 'student' 179 180 def isApplicant(self): 181 usertype = getattr(self.request.principal, 'user_type', None) 182 if not usertype: 183 return False 184 return self.request.principal.user_type == 'applicant' 174 185 175 186 def getStudentName(self): … … 180 191 return 181 192 182 def isStudent(self): 183 prm = get_principal_role_manager() 184 roles = [x[0] for x in 185 prm.getRolesForPrincipal(self.request.principal.id)] 186 return 'waeup.Student' in roles 193 def getApplicantName(self): 194 """Return the applicant name. 195 """ 196 if IApplicant.providedBy(self.context): 197 return self.context.fullname 198 return 187 199 188 200 def update(self): … … 209 221 210 222 def render(self): 211 if self.isStudent() or not self.isAuthenticated():223 if self.isStudent() or self.isApplicant() or not self.isAuthenticated(): 212 224 return self.studenttemp.render(self) 213 225 return self.stafftemp.render(self) -
main/waeup.sirp/trunk/src/waeup/sirp/browser/pages.py
r7234 r7240 150 150 camefrom = None 151 151 152 def isStudent(self):153 prm = get_principal_role_manager()154 roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]155 return 'waeup.Student' in roles156 157 152 def update(self, SUBMIT=None, camefrom=None): 158 153 self.camefrom = camefrom … … 160 155 if self.request.principal.id != 'zope.anybody': 161 156 self.flash('You logged in.') 162 if self. isStudent():157 if self.request.principal.user_type == 'student': 163 158 rel_link = '/students/%s' % self.request.principal.id 159 self.redirect(self.application_url() + rel_link) 160 return 161 elif self.request.principal.user_type == 'applicant': 162 container, application_number = self.request.principal.id.split('_') 163 rel_link = '/applicants/%s/%s' % ( 164 container, application_number) 164 165 self.redirect(self.application_url() + rel_link) 165 166 return … … 222 223 email_from = self.config.email_admin 223 224 username = self.request.principal.id 225 usertype = getattr(self.request.principal, 226 'user_type', 'system').title() 224 227 body = data['body'] 225 228 email_to = self.config.email_admin 226 229 subject = self.config.email_subject 227 success = send_mail(fullname,username, self.config.name,230 success = send_mail(fullname,username,usertype,self.config.name, 228 231 body,email_from,email_to,subject) 229 232 if success: … … 248 251 email_to = self.config.email_admin 249 252 subject = self.config.email_subject 250 success = send_mail(fullname,username,self.config.name, 253 usertype = u'Anonymous' 254 success = send_mail(fullname,username,usertype,self.config.name, 251 255 body,email_from,email_to,subject) 252 256 if success: … … 467 471 email_from = self.config.email_admin 468 472 username = self.request.principal.id 473 usertype = getattr(self.request.principal, 474 'user_type', 'system').title() 469 475 body = data['body'] 470 476 email_to = self.context.email 471 477 subject = self.config.email_subject 472 success = send_mail(fullname,username, self.config.name,478 success = send_mail(fullname,username,usertype,self.config.name, 473 479 body,email_from,email_to,subject) 474 480 if success: -
main/waeup.sirp/trunk/src/waeup/sirp/browser/static/waeup-base.css
r7135 r7240 70 70 71 71 .studentmenu { 72 text-align: center;72 text-align: left; 73 73 line-height: 2; 74 74 } -
main/waeup.sirp/trunk/src/waeup/sirp/browser/templates/studentsitelayout.pt
r7184 r7240 56 56 57 57 <div class="yui-b"> 58 <div class="block" tal:condition="layout/getStudentName">58 <div class="block" > 59 59 <div class="studentmenu"> 60 <tal:left content="structure provider:top_student" /> 60 <div tal:condition="layout/getStudentName" 61 tal:replace="structure provider:top_student"> 62 Student Links 63 </div> 64 <div tal:content="structure provider:actionbar"> 65 Student Actions 66 </div> 61 67 </div> 62 68 </div> … … 89 95 <div tal:replace="structure view/content"> THE CONTENT 90 96 </div> 91 <span tal:replace="structure provider:actionbar"></span>92 97 </div> 93 98 </div> -
main/waeup.sirp/trunk/src/waeup/sirp/permissions.py
r7228 r7240 115 115 'waeup.manageApplication', 'waeup.handleApplication', 116 116 'waeup.viewStudent', 'waeup.manageStudent', 'clearStudent', 117 'waeup.uploadStudentFile', 'waeup.viewStudents',117 'waeup.uploadStudentFile', 118 118 'waeup.viewHostels', 'waeup.manageHostels', 119 'waeup.showStudents') 119 'waeup.showStudents', 120 'waeup.viewStudentsContainer','waeup.viewStudentsTab', 121 ) 120 122 121 123 def get_all_roles(): -
main/waeup.sirp/trunk/src/waeup/sirp/students/authentication.py
r7233 r7240 60 60 61 61 @property 62 def user_type(self): 63 return u'student' 64 65 @property 62 66 def description(self): 63 67 return self.title … … 126 130 return None 127 131 account = self.getAccount(credentials['login']) 128 129 132 if account is None: 130 133 return None … … 135 138 description=account.description, 136 139 email=account.email, 137 phone=account.phone) 140 phone=account.phone, 141 user_type=account.user_type) 138 142 139 143 def principalInfo(self, id): … … 266 270 """ 267 271 grok.implements(IAuthPluginUtility) 272 grok.name('students_auth_setup') 268 273 269 274 def register(self, pau): -
main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
r7234 r7240 161 161 grok.context(IStudentsContainer) 162 162 grok.name('index') 163 grok.require('waeup.viewStudents ')163 grok.require('waeup.viewStudentsContainer') 164 164 grok.template('containerpage') 165 165 label = 'Student Section' … … 381 381 email_from = self.config.email_admin 382 382 username = self.request.principal.id 383 usertype = self.request.principal.user_type.title() 383 384 body = data['body'] 384 385 #subject = u'Mail from SIRP' 385 386 subject = data['subject'] 386 387 email_to = self.context.email 387 success = send_mail(fullname,username, self.config.name,388 success = send_mail(fullname,username,usertype,self.config.name, 388 389 body,email_from,email_to,subject) 389 390 if success: … … 1271 1272 return 1272 1273 1273 @property1274 def is_student(self):1275 prm = get_principal_role_manager()1276 roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]1277 return 'waeup.Student' in roles1278 1279 1274 @grok.action('Remove selected') 1280 1275 def delBedTickets(self, **data): 1281 if self.is_student:1276 if getattr(self.request.principal, 'user_type', None) == 'student': 1282 1277 self.flash('You are not allowed to remove bed tickets.') 1283 1278 self.redirect(self.url(self.context)) … … 1305 1300 def selected_actions(self): 1306 1301 sa = self.actions 1307 if self.is_student:1302 if getattr(self.request.principal, 'user_type', None) == 'student': 1308 1303 sa = [action for action in self.actions 1309 1304 if not action.label in self.officers_only_actions] -
main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/accommodationmanagepage.pt
r7017 r7240 18 18 <thead> 19 19 <tr> 20 <th tal:condition="not: view/is_student"> </th>20 <th tal:condition="not: layout/isStudent"> </th> 21 21 <th>Session</th> 22 22 <th>Booking Date</th> … … 27 27 <tbody> 28 28 <tr tal:repeat="cl context/values"> 29 <td tal:condition="not: view/is_student">29 <td tal:condition="not: layout/isStudent"> 30 30 <input type="checkbox" 31 31 name="val_id" -
main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py
r7221 r7240 123 123 124 124 student_id = schema.TextLine( 125 title = u'Student I D',125 title = u'Student Id', 126 126 required = False, 127 127 ) -
main/waeup.sirp/trunk/src/waeup/sirp/students/permissions.py
r7201 r7240 29 29 grok.name('waeup.viewStudent') 30 30 31 class ViewStudents(grok.Permission): 32 grok.name('waeup.viewStudents') 31 class ViewStudentsTab(grok.Permission): 32 grok.name('waeup.viewStudentsTab') 33 34 class ViewMyStudentDataTab(grok.Permission): 35 grok.name('waeup.viewMyStudentDataTab') 36 37 class ViewStudentsContainer(grok.Permission): 38 grok.name('waeup.viewStudentsContainer') 33 39 34 40 class PayStudent(grok.Permission): … … 58 64 class StudentRole(grok.Role): 59 65 grok.name('waeup.Student') 60 grok.permissions('waeup.viewAcademics' )66 grok.permissions('waeup.viewAcademics', 'waeup.viewMyStudentDataTab') 61 67 62 68 class StudentsOfficer(grok.Role): 63 69 grok.name('waeup.StudentsOfficer') 64 70 grok.title(u'Students Officer (view only)') 65 grok.permissions('waeup.viewStudent','waeup.viewStudents') 71 grok.permissions('waeup.viewStudent','waeup.viewStudents', 72 'waeup.viewStudentsTab', 'waeup.viewStudentsContainer') 66 73 67 74 class StudentsManager(grok.Role): -
main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py
r7218 r7240 693 693 self.browser.getControl(name="form.password").value = 'mrclearsecret' 694 694 self.browser.getControl("Login").click() 695 self.assert True('You logged in' inself.browser.contents)695 self.assertMatches('...You logged in...', self.browser.contents) 696 696 # CO can see his roles 697 697 self.browser.getLink("My Roles").click() -
main/waeup.sirp/trunk/src/waeup/sirp/students/viewlets.py
r7191 r7240 163 163 grok.context(IWAeUPObject) 164 164 grok.order(4) 165 grok.require('waeup.viewStudents ')165 grok.require('waeup.viewStudentsTab') 166 166 grok.template('primarynavtab') 167 167 … … 200 200 return '' 201 201 202 #class HomeTab(PrimaryStudentNavTab): 203 # """Home-tab in primary navigation. 204 # """ 205 # grok.order(1) 206 # grok.require('waeup.Authenticated') 207 # pnav = 0 208 # tab_title = u'Home' 209 210 #class ProspectusTab(PrimaryStudentNavTab): 211 # """Faculties-tab in primary navigation. 212 # """ 213 # grok.order(2) 214 # grok.require('waeup.viewAcademics') 215 # pnav = 1 216 # tab_title = u'Prospectus' 217 218 # @property 219 # def link_target(self): 220 # return self.view.application_url('faculties') 221 222 class MyDataTab(PrimaryStudentNavTab): 202 class MyStudentDataTab(PrimaryStudentNavTab): 223 203 """MyData-tab in primary navigation. 224 204 """ 225 205 grok.order(3) 226 grok.require('waeup. Authenticated')206 grok.require('waeup.viewMyStudentDataTab') 227 207 pnav = 4 228 208 tab_title = u'My Data' -
main/waeup.sirp/trunk/src/waeup/sirp/utils/helpers.py
r7234 r7240 481 481 return account 482 482 483 def send_mail(fullname,username, portal,body,email_from,email_to,subject):483 def send_mail(fullname,username,usertype,portal,body,email_from,email_to,subject): 484 484 """Send an email with data provided by forms. 485 485 """ … … 487 487 text = """Fullname: %s 488 488 User Id: %s 489 User Type: %s 489 490 Portal: %s 490 491 491 492 %s 492 493 """ 493 msg = MIMEText(text % (fullname,username, portal,body))494 msg = MIMEText(text % (fullname,username,usertype,portal,body)) 494 495 msg['From'] = '%s <%s>' % (fullname,email_from) 495 496 msg['To'] = email_to
Note: See TracChangeset for help on using the changeset viewer.