Ignore:
Timestamp:
14 Oct 2012, 21:02:31 (12 years ago)
Author:
Henrik Bettermann
Message:

Dedicated officers should be able to login as student with a temporary password set by the system. This is the first part of its implementation.

Location:
main/waeup.kofa/trunk/src/waeup/kofa
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py

    r9326 r9334  
    2323import re
    2424import sys
     25from datetime import datetime, timedelta
    2526from urllib import urlencode
    2627from zope import schema
     
    230231                return
    231232            # Display appropriate flash message if credentials are correct
    232             # but student has been deactivated.
     233            # but student has been deactivated or a temporary password
     234            # has been set.
    233235            login = self.request.form['form.login']
    234236            if len(login) == 8 and grok.getSite()['students'].has_key(login):
     
    238240                if student.password is not None and \
    239241                    passwordmanager.checkPassword(student.password, password):
     242                    # The student entered valid credentials.
     243                    # First we check if a temporary password has been set.
     244                    delta = timedelta(minutes=10)
     245                    now = datetime.utcnow()
     246                    temp_password_dict = getattr(student, 'temp_password', None)
     247                    if temp_password_dict is not None and \
     248                        now < temp_password_dict.get('timestamp', now) + delta:
     249                        self.flash(
     250                            _('Your account has been temporarily deactivated.'))
     251                        return
     252                    # Now we know that the student is suspended.
    240253                    self.flash(_('Your account has been deactivated.'))
    241254                    return
    242255            self.flash(_('You entered invalid credentials.'))
     256            return
    243257
    244258
     
    919933${e}
    920934
    921 """, mapping = {'a':normalized_filename,
     935Comment by Import Manager:""", mapping = {'a':normalized_filename,
    922936                'b':importer,
    923937                'c':import_mode,
  • main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py

    r8983 r9334  
    101101
    102102    def checkPassword(self, password):
    103         """Check whether the given `password` matches the one stored.
     103        """Check whether the given `password` matches the one stored by
     104        students or the temporary password set by officers.
    104105
    105106        We additionally check if student account has been suspended.
     
    107108        if not isinstance(password, basestring):
    108109            return False
     110        passwordmanager = getUtility(IPasswordManager, 'SSHA')
     111        temp_password = self.context.getTempPassword()
     112        if temp_password:
     113            return passwordmanager.checkPassword(temp_password, password)
    109114        if not getattr(self.context, 'password', None):
    110115            # unset/empty passwords do never match
     
    112117        if self.context.suspended == True:
    113118            return False
    114         passwordmanager = getUtility(IPasswordManager, 'SSHA')
    115         return passwordmanager.checkPassword(
    116             self.context.password, password)
     119        return passwordmanager.checkPassword(self.context.password, password)
    117120
    118121class StudentsAuthenticatorPlugin(grok.GlobalUtility):
  • main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py

    r9320 r9334  
    150150    state = Attribute('Returns the registration state of a student')
    151151    password = Attribute('Encrypted password of a student')
     152    temp_password = Attribute(
     153        'Dictionary with user name, timestamp and encrypted password')
    152154    certcode = Attribute('The certificate code of any chosen study course')
    153155    depcode = Attribute('The department code of any chosen study course')
     
    224226        )
    225227
     228    def setTempPassword(user, password):
     229        """Set a temporary password (LDAP-compatible) SSHA encoded for
     230        officers.
     231
     232        """
     233
     234    def getTempPassword():
     235        """Check if a temporary password has been set and if it
     236        is not expired.
     237
     238        Return the temporary password if valid,
     239        None otherwise. Unset the temporary password if expired.
     240        """
     241
    226242    def transfer(certificate, current_session,
    227243        current_level, current_verdict):
  • main/waeup.kofa/trunk/src/waeup/kofa/students/student.py

    r9183 r9334  
    2323import shutil
    2424import grok
     25from datetime import datetime, timedelta
    2526from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
     27from zope.password.interfaces import IPasswordManager
    2628from zope.component import getUtility, createObject
    2729from zope.component.interfaces import IFactory
     
    5254    grok.provides(IStudent)
    5355
     56    temp_password_minutes = 10
     57
    5458    def __init__(self):
    5559        super(Student, self).__init__()
     
    6064            self.student_id = u'Z654321'
    6165        self.password = None
    62         return
     66        self.temp_password = None
     67        return
     68
     69    def setTempPassword(self, user, password):
     70        """Set a temporary password (LDAP-compatible) SSHA encoded for
     71        officers.
     72
     73        """
     74        passwordmanager = getUtility(IPasswordManager, 'SSHA')
     75        self.temp_password = {}
     76        self.temp_password[
     77            'password'] = passwordmanager.encodePassword(password)
     78        self.temp_password['user'] = user
     79        self.temp_password['timestamp'] = datetime.utcnow() # offset-naive datetime
     80
     81    def getTempPassword(self):
     82        """Check if a temporary password has been set and if it
     83        is not expired.
     84
     85        Return the temporary password if valid,
     86        None otherwise. Unset the temporary password if expired.
     87        """
     88        temp_password_dict = getattr(self, 'temp_password', None)
     89        if temp_password_dict is not None:
     90            delta = timedelta(minutes=self.temp_password_minutes)
     91            now = datetime.utcnow()
     92            if now < temp_password_dict.get('timestamp') + delta:
     93                return temp_password_dict.get('password')
     94            else:
     95                # Unset temporary password if expired
     96                self.temp_password = None
     97        return None
    6398
    6499    def writeLogMessage(self, view, message):
  • main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_authentication.py

    r8983 r9334  
    1717##
    1818import unittest
     19from datetime import datetime, timedelta
    1920from zope.authentication.interfaces import IAuthentication
    2021from zope.component import provideUtility, queryUtility, getGlobalSiteManager
     
    6667    phone = None
    6768    suspended = False
     69    temp_password_minutes = 10
     70
     71    def setTempPassword(self, user, password):
     72        passwordmanager = queryUtility(IPasswordManager, 'SSHA')
     73        self.temp_password = {}
     74        self.temp_password[
     75            'password'] = passwordmanager.encodePassword(password)
     76        self.temp_password['user'] = user
     77        self.temp_password['timestamp'] = datetime.utcnow()
     78
     79    def getTempPassword(self):
     80        temp_password_dict = getattr(self, 'temp_password', None)
     81        if temp_password_dict is not None:
     82            delta = timedelta(minutes=self.temp_password_minutes)
     83            now = datetime.utcnow()
     84            if now < temp_password_dict.get('timestamp') + delta:
     85                return temp_password_dict.get('password')
     86            else:
     87                # Unset temporary password if expired
     88                self.temp_password = None
     89        return None
    6890
    6991
     
    138160        return
    139161
     162    def test_check_temp_password(self):
     163        # make sure that, if a temp password is set,
     164        # this password is used for authentication
     165        self.account.setPassword('secret')
     166        self.fake_stud.setTempPassword(user='beate', password='temp_secret')
     167        result1 = self.account.checkPassword('secret')
     168        result2 = self.account.checkPassword(None)
     169        result3 = self.account.checkPassword('nonsense')
     170        result4 = self.account.checkPassword('temp_secret')
     171        self.assertEqual(result1, False)
     172        self.assertEqual(result2, False)
     173        self.assertEqual(result3, False)
     174        self.assertEqual(result4, True)
     175        # if the temp password is expired, the original password
     176        # is used again
     177        delta = timedelta(minutes=11)
     178        self.fake_stud.temp_password['timestamp'] = datetime.utcnow() - delta
     179        result5 = self.account.checkPassword('temp_secret')
     180        result6 = self.account.checkPassword('secret')
     181        self.assertEqual(result5, False)
     182        self.assertEqual(result6, True)
     183        return
     184
    140185    def test_check_unset_password(self):
    141186        # empty and unset passwords do not match anything
  • main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py

    r9332 r9334  
    11831183        return
    11841184
    1185     def test_student_access(self):
     1185    def test_student_login(self):
    11861186        # Student cant login if their password is not set
    11871187        self.student.password = None
     
    11951195        IUserAccount(
    11961196            self.app['students'][self.student_id]).setPassword('spwd')
    1197         IWorkflowInfo(self.student).fireTransition('admit')
    11981197        # Students can't login if their account is suspended/deactivated
    11991198        self.student.suspended = True
     
    12021201        self.browser.getControl(name="form.password").value = 'spwd'
    12031202        self.browser.getControl("Login").click()
    1204         self.assertTrue(
    1205             'Your account has been deactivated.' in self.browser.contents)
     1203        self.assertMatches(
     1204            '...Your account has been deactivated...', self.browser.contents)
    12061205        self.student.suspended = False
     1206        # Students can't login if a temporary password has been set and
     1207        # is not expired
     1208        self.app['students'][self.student_id].setTempPassword(
     1209            'anybody', 'temp_spwd')
     1210        self.browser.open(self.login_path)
     1211        self.browser.getControl(name="form.login").value = self.student_id
     1212        self.browser.getControl(name="form.password").value = 'spwd'
    12071213        self.browser.getControl("Login").click()
    1208         self.assertTrue(
    1209             'You logged in.' in self.browser.contents)
     1214        self.assertMatches(
     1215            '...Your account has been temporarily deactivated...',
     1216            self.browser.contents)
     1217        # The student can login with the temporary password
     1218        self.browser.open(self.login_path)
     1219        self.browser.getControl(name="form.login").value = self.student_id
     1220        self.browser.getControl(name="form.password").value = 'temp_spwd'
     1221        self.browser.getControl("Login").click()
     1222        self.assertMatches(
     1223            '...You logged in...', self.browser.contents)
     1224        # Student can view the base data
     1225        self.browser.open(self.student_path)
     1226        self.assertEqual(self.browser.headers['Status'], '200 Ok')
     1227        self.assertEqual(self.browser.url, self.student_path)
     1228        # When the password expires ...
     1229        delta = timedelta(minutes=11)
     1230        self.app['students'][self.student_id].temp_password[
     1231            'timestamp'] = datetime.utcnow() - delta
     1232        self.app['students'][self.student_id]._p_changed = True
     1233        # ... the student will be automatically logged out
     1234        self.assertRaises(
     1235            Unauthorized, self.browser.open, self.student_path)
     1236        # Then the student can login with the original password
     1237        self.browser.open(self.login_path)
     1238        self.browser.getControl(name="form.login").value = self.student_id
     1239        self.browser.getControl(name="form.password").value = 'spwd'
     1240        self.browser.getControl("Login").click()
     1241        self.assertMatches(
     1242            '...You logged in...', self.browser.contents)
     1243
     1244    def test_student_access(self):
     1245        # Student cant login if their password is not set
     1246        IWorkflowInfo(self.student).fireTransition('admit')
     1247        self.browser.open(self.login_path)
     1248        self.browser.getControl(name="form.login").value = self.student_id
     1249        self.browser.getControl(name="form.password").value = 'spwd'
     1250        self.browser.getControl("Login").click()
     1251        self.assertMatches(
     1252            '...You logged in...', self.browser.contents)
    12101253        # Admitted student can upload a passport picture
    12111254        self.browser.open(self.student_path + '/change_portrait')
Note: See TracChangeset for help on using the changeset viewer.