Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 9334)
@@ -23,4 +23,5 @@
 import re
 import sys
+from datetime import datetime, timedelta
 from urllib import urlencode
 from zope import schema
@@ -230,5 +231,6 @@
                 return
             # Display appropriate flash message if credentials are correct
-            # but student has been deactivated.
+            # but student has been deactivated or a temporary password
+            # has been set.
             login = self.request.form['form.login']
             if len(login) == 8 and grok.getSite()['students'].has_key(login):
@@ -238,7 +240,19 @@
                 if student.password is not None and \
                     passwordmanager.checkPassword(student.password, password):
+                    # The student entered valid credentials.
+                    # First we check if a temporary password has been set.
+                    delta = timedelta(minutes=10)
+                    now = datetime.utcnow()
+                    temp_password_dict = getattr(student, 'temp_password', None)
+                    if temp_password_dict is not None and \
+                        now < temp_password_dict.get('timestamp', now) + delta:
+                        self.flash(
+                            _('Your account has been temporarily deactivated.'))
+                        return
+                    # Now we know that the student is suspended.
                     self.flash(_('Your account has been deactivated.'))
                     return
             self.flash(_('You entered invalid credentials.'))
+            return
 
 
@@ -919,5 +933,5 @@
 ${e}
 
-""", mapping = {'a':normalized_filename,
+Comment by Import Manager:""", mapping = {'a':normalized_filename,
                 'b':importer,
                 'c':import_mode,
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py	(revision 9334)
@@ -101,5 +101,6 @@
 
     def checkPassword(self, password):
-        """Check whether the given `password` matches the one stored.
+        """Check whether the given `password` matches the one stored by
+        students or the temporary password set by officers.
 
         We additionally check if student account has been suspended.
@@ -107,4 +108,8 @@
         if not isinstance(password, basestring):
             return False
+        passwordmanager = getUtility(IPasswordManager, 'SSHA')
+        temp_password = self.context.getTempPassword()
+        if temp_password:
+            return passwordmanager.checkPassword(temp_password, password)
         if not getattr(self.context, 'password', None):
             # unset/empty passwords do never match
@@ -112,7 +117,5 @@
         if self.context.suspended == True:
             return False
-        passwordmanager = getUtility(IPasswordManager, 'SSHA')
-        return passwordmanager.checkPassword(
-            self.context.password, password)
+        return passwordmanager.checkPassword(self.context.password, password)
 
 class StudentsAuthenticatorPlugin(grok.GlobalUtility):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 9334)
@@ -150,4 +150,6 @@
     state = Attribute('Returns the registration state of a student')
     password = Attribute('Encrypted password of a student')
+    temp_password = Attribute(
+        'Dictionary with user name, timestamp and encrypted password')
     certcode = Attribute('The certificate code of any chosen study course')
     depcode = Attribute('The department code of any chosen study course')
@@ -224,4 +226,18 @@
         )
 
+    def setTempPassword(user, password):
+        """Set a temporary password (LDAP-compatible) SSHA encoded for
+        officers.
+
+        """
+
+    def getTempPassword():
+        """Check if a temporary password has been set and if it
+        is not expired.
+
+        Return the temporary password if valid,
+        None otherwise. Unset the temporary password if expired.
+        """
+
     def transfer(certificate, current_session,
         current_level, current_verdict):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/student.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/student.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/student.py	(revision 9334)
@@ -23,5 +23,7 @@
 import shutil
 import grok
+from datetime import datetime, timedelta
 from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
+from zope.password.interfaces import IPasswordManager
 from zope.component import getUtility, createObject
 from zope.component.interfaces import IFactory
@@ -52,4 +54,6 @@
     grok.provides(IStudent)
 
+    temp_password_minutes = 10
+
     def __init__(self):
         super(Student, self).__init__()
@@ -60,5 +64,36 @@
             self.student_id = u'Z654321'
         self.password = None
-        return
+        self.temp_password = None
+        return
+
+    def setTempPassword(self, user, password):
+        """Set a temporary password (LDAP-compatible) SSHA encoded for
+        officers.
+
+        """
+        passwordmanager = getUtility(IPasswordManager, 'SSHA')
+        self.temp_password = {}
+        self.temp_password[
+            'password'] = passwordmanager.encodePassword(password)
+        self.temp_password['user'] = user
+        self.temp_password['timestamp'] = datetime.utcnow() # offset-naive datetime
+
+    def getTempPassword(self):
+        """Check if a temporary password has been set and if it
+        is not expired. 
+
+        Return the temporary password if valid,
+        None otherwise. Unset the temporary password if expired.
+        """
+        temp_password_dict = getattr(self, 'temp_password', None)
+        if temp_password_dict is not None:
+            delta = timedelta(minutes=self.temp_password_minutes)
+            now = datetime.utcnow()
+            if now < temp_password_dict.get('timestamp') + delta:
+                return temp_password_dict.get('password')
+            else:
+                # Unset temporary password if expired
+                self.temp_password = None
+        return None
 
     def writeLogMessage(self, view, message):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_authentication.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_authentication.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_authentication.py	(revision 9334)
@@ -17,4 +17,5 @@
 ##
 import unittest
+from datetime import datetime, timedelta
 from zope.authentication.interfaces import IAuthentication
 from zope.component import provideUtility, queryUtility, getGlobalSiteManager
@@ -66,4 +67,25 @@
     phone = None
     suspended = False
+    temp_password_minutes = 10
+
+    def setTempPassword(self, user, password):
+        passwordmanager = queryUtility(IPasswordManager, 'SSHA')
+        self.temp_password = {}
+        self.temp_password[
+            'password'] = passwordmanager.encodePassword(password)
+        self.temp_password['user'] = user
+        self.temp_password['timestamp'] = datetime.utcnow()
+
+    def getTempPassword(self):
+        temp_password_dict = getattr(self, 'temp_password', None)
+        if temp_password_dict is not None:
+            delta = timedelta(minutes=self.temp_password_minutes)
+            now = datetime.utcnow()
+            if now < temp_password_dict.get('timestamp') + delta:
+                return temp_password_dict.get('password')
+            else:
+                # Unset temporary password if expired
+                self.temp_password = None
+        return None
 
 
@@ -138,4 +160,27 @@
         return
 
+    def test_check_temp_password(self):
+        # make sure that, if a temp password is set,
+        # this password is used for authentication
+        self.account.setPassword('secret')
+        self.fake_stud.setTempPassword(user='beate', password='temp_secret')
+        result1 = self.account.checkPassword('secret')
+        result2 = self.account.checkPassword(None)
+        result3 = self.account.checkPassword('nonsense')
+        result4 = self.account.checkPassword('temp_secret')
+        self.assertEqual(result1, False)
+        self.assertEqual(result2, False)
+        self.assertEqual(result3, False)
+        self.assertEqual(result4, True)
+        # if the temp password is expired, the original password
+        # is used again
+        delta = timedelta(minutes=11)
+        self.fake_stud.temp_password['timestamp'] = datetime.utcnow() - delta
+        result5 = self.account.checkPassword('temp_secret')
+        result6 = self.account.checkPassword('secret')
+        self.assertEqual(result5, False)
+        self.assertEqual(result6, True)
+        return
+
     def test_check_unset_password(self):
         # empty and unset passwords do not match anything
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 9333)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 9334)
@@ -1183,5 +1183,5 @@
         return
 
-    def test_student_access(self):
+    def test_student_login(self):
         # Student cant login if their password is not set
         self.student.password = None
@@ -1195,5 +1195,4 @@
         IUserAccount(
             self.app['students'][self.student_id]).setPassword('spwd')
-        IWorkflowInfo(self.student).fireTransition('admit')
         # Students can't login if their account is suspended/deactivated
         self.student.suspended = True
@@ -1202,10 +1201,54 @@
         self.browser.getControl(name="form.password").value = 'spwd'
         self.browser.getControl("Login").click()
-        self.assertTrue(
-            'Your account has been deactivated.' in self.browser.contents)
+        self.assertMatches(
+            '...Your account has been deactivated...', self.browser.contents)
         self.student.suspended = False
+        # Students can't login if a temporary password has been set and
+        # is not expired
+        self.app['students'][self.student_id].setTempPassword(
+            'anybody', 'temp_spwd')
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
         self.browser.getControl("Login").click()
-        self.assertTrue(
-            'You logged in.' in self.browser.contents)
+        self.assertMatches(
+            '...Your account has been temporarily deactivated...',
+            self.browser.contents)
+        # The student can login with the temporary password
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'temp_spwd'
+        self.browser.getControl("Login").click()
+        self.assertMatches(
+            '...You logged in...', self.browser.contents)
+        # Student can view the base data
+        self.browser.open(self.student_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.student_path)
+        # When the password expires ...
+        delta = timedelta(minutes=11)
+        self.app['students'][self.student_id].temp_password[
+            'timestamp'] = datetime.utcnow() - delta
+        self.app['students'][self.student_id]._p_changed = True
+        # ... the student will be automatically logged out
+        self.assertRaises(
+            Unauthorized, self.browser.open, self.student_path)
+        # Then the student can login with the original password
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
+        self.browser.getControl("Login").click()
+        self.assertMatches(
+            '...You logged in...', self.browser.contents)
+
+    def test_student_access(self):
+        # Student cant login if their password is not set
+        IWorkflowInfo(self.student).fireTransition('admit')
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
+        self.browser.getControl("Login").click()
+        self.assertMatches(
+            '...You logged in...', self.browser.contents)
         # Admitted student can upload a passport picture
         self.browser.open(self.student_path + '/change_portrait')
