Index: /main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 15606)
@@ -364,10 +364,27 @@
                         self.request.principal.id]
                     rel_link = '/students/%s' % self.request.principal.id
-                    if student.personal_data_expired:
-                        rel_link = '/students/%s/edit_personal' % (
-                            self.request.principal.id)
-                        self.flash(
-                          _('Your personal data record is outdated. Please update.'),
-                          type='warning')
+                    if student.getParentsPassword():
+                        # Add logging message
+                        student.writeLogMessage(self, 'Parents logged in')
+                        # Replace role if parents have logged in
+                        role_manager = IPrincipalRoleManager(student)
+                        role_manager.removeRoleFromPrincipal(
+                            'waeup.local.StudentRecordOwner', student.student_id)
+                        notify(LocalRoleSetEvent(
+                            student, 'waeup.local.StudentRecordOwner',
+                            student.student_id, granted=False))
+                        role_manager.assignRoleToPrincipal(
+                            'waeup.local.Parents', student.student_id)
+                        notify(LocalRoleSetEvent(
+                            student, 'waeup.local.Parents',
+                            student.student_id, granted=True))
+                    else:
+                        # Redirect to personal page if data are outdated
+                        if student.personal_data_expired:
+                            rel_link = '/students/%s/edit_personal' % (
+                                self.request.principal.id)
+                            self.flash(
+                              _('Your personal data record is outdated. Please update.'),
+                              type='warning')
                     self.redirect(self.application_url() + rel_link)
                     return
@@ -402,6 +419,6 @@
                 return
             # Display appropriate flash message if credentials are correct
-            # but student has been deactivated or a temporary password
-            # has been set.
+            # but student has been deactivated or a temporary or parents 
+            # password has been set.
             login = self.request.form['form.login']
             if len(login) == 8 and login in grok.getSite()['students']:
@@ -413,11 +430,14 @@
                     # 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:
+                    if student.getTempPassword():
                         self.flash(
                             _('Your account has been temporarily deactivated.'),
+                            type='warning')
+                        return
+                    # Next we check if a parents password has been set.
+                    if student.getParentsPassword():
+                        self.flash(
+                            _('Your account has been temporarily deactivated '
+                              'because your parents have logged in.'),
                             type='warning')
                         return
Index: /main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/doctests/permissions.txt	(revision 15606)
@@ -42,5 +42,5 @@
 
     >>> len(list(get_waeup_roles(also_local=True)))
-    52
+    53
 
 
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/authentication.py	(revision 15606)
@@ -112,5 +112,6 @@
     def checkPassword(self, password):
         """Check whether the given `password` matches the one stored by
-        students or the temporary password set by officers.
+        students, the temporary password was set by officers or
+        the parents_password was set by parents.
         We additionally check if student account has been suspended
         or if the portal is blocked.
@@ -128,4 +129,7 @@
         if temp_password:
             return passwordmanager.checkPassword(temp_password, password)
+        parents_password = self.context.getParentsPassword()
+        if parents_password:
+            return passwordmanager.checkPassword(parents_password, password)
         if not getattr(self.context, 'password', None):
             # unset/empty passwords do never match
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 15606)
@@ -226,4 +226,5 @@
     password = Attribute('Encrypted password')
     temp_password = Attribute('Dictionary with user name, timestamp and encrypted password')
+    parents_password = Attribute('Dictionary with student_id, timestamp and encrypted password')
 
     suspended = schema.Bool(
@@ -314,4 +315,17 @@
         is not expired. Return the temporary password if valid,
         None otherwise. Unset the temporary password if expired.
+        """
+
+    def setParentsPassword(password):
+        """Set a parents password (LDAP-compatible) SSHA encoded for
+        parents.
+        """
+
+    def getParentsPassword():
+        """Check if a parents password has been set and if it
+        is not expired.
+
+        Return the parents password if valid,
+        None otherwise. Unset the parents password if expired.
         """
 
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py	(revision 15606)
@@ -122,4 +122,11 @@
                      'waeup.handleAccommodation',
                      'waeup.editStudyLevel')
+
+class Parents(grok.Role):
+    """Parents temporarily get access to view the records of their children.
+    """
+    grok.name('waeup.local.Parents')
+    grok.title(u'Parents')
+    grok.permissions('waeup.viewStudent')
 
 # Site Roles
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/student.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/student.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/student.py	(revision 15606)
@@ -31,5 +31,7 @@
 from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from zope.schema.interfaces import ConstraintNotSatisfied
-
+from zope.event import notify
+
+from waeup.kofa.authentication import LocalRoleSetEvent
 from waeup.kofa.image import KofaImageFile
 from waeup.kofa.imagestorage import DefaultFileStoreHandler
@@ -67,4 +69,5 @@
         self.password = None
         self.temp_password = None
+        self.parents_password = None
         return
 
@@ -96,4 +99,45 @@
                 # Unset temporary password if expired
                 self.temp_password = None
+        return None
+
+    def setParentsPassword(self, password):
+        """Set a temporary password (LDAP-compatible) SSHA encoded for
+        parents.
+        """
+        passwordmanager = getUtility(IPasswordManager, 'SSHA')
+        self.parents_password = {}
+        self.parents_password[
+            'password'] = passwordmanager.encodePassword(password)
+        self.parents_password['timestamp'] = datetime.utcnow() # offset-naive datetime
+
+    def getParentsPassword(self):
+        """Check if a parents password has been set and if it
+        is not expired.
+
+        Return the parents password if valid,
+        None otherwise. Unset the parents password and replace roles
+        if expired.
+        """
+        parents_password_dict = getattr(self, 'parents_password', None)
+        if parents_password_dict is not None:
+            delta = timedelta(minutes=self.temp_password_minutes)
+            now = datetime.utcnow()
+            if now < parents_password_dict.get('timestamp') + delta:
+                return parents_password_dict.get('password')
+            else:
+                # Unset parents password if expired
+                self.parents_password = None
+                # Replace roles if expired
+                role_manager = IPrincipalRoleManager(self)
+                role_manager.removeRoleFromPrincipal(
+                    'waeup.local.Parents', self.student_id)
+                notify(LocalRoleSetEvent(
+                    self, 'waeup.local.Parents',
+                    self.student_id, granted=False))
+                role_manager.assignRoleToPrincipal(
+                    'waeup.local.StudentRecordOwner', self.student_id)
+                notify(LocalRoleSetEvent(
+                    self, 'waeup.local.StudentRecordOwner',
+                    self.student_id, granted=True))
         return None
 
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 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_authentication.py	(revision 15606)
@@ -89,4 +89,17 @@
         return None
 
+    def getParentsPassword(self):
+        parents_password_dict = getattr(self, 'parents_password', None)
+        if parents_password_dict is not None:
+            delta = timedelta(minutes=self.temp_password_minutes)
+            now = datetime.utcnow()
+            if now < parents_password_dict.get('timestamp') + delta:
+                return parents_password_dict.get('password')
+            else:
+                # Unset temporary password if expired
+                self.parents_password = None
+        return None
+
+
 
 class MinimalPAU(PluggableAuthentication):
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 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 15606)
@@ -4671,2 +4671,58 @@
             self.assertTrue(
                 'Uploaded file contains illegal data' in self.browser.contents)
+
+class ParentsUITests(StudentsFullSetup):
+    # Tests for UI actions when acting as parents.
+
+    def test_login_as_parents(self):
+        # Sstudent login still works after all the changes made
+        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.browser.open(self.edit_personal_path)
+        self.browser.getLink("Logout").click()
+        self.assertTrue('You have been logged out' in self.browser.contents)
+        # We set parents password
+        self.app['students'][self.student_id].setParentsPassword('ppwd')
+        self.browser.open(self.login_path)
+        # Student can't login with original password
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'spwd'
+        self.browser.getControl("Login").click()
+        self.assertEqual(self.browser.url, self.login_path)
+        self.assertTrue('Your account has been temporarily deactivated '
+            'because your parents have logged in.' in self.browser.contents)
+        # Parents can login with their password
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = self.student_id
+        self.browser.getControl(name="form.password").value = 'ppwd'
+        self.browser.getControl("Login").click()
+        self.assertTrue(
+            'You logged in.' in self.browser.contents)
+        self.assertTrue(
+            '<a href="http://localhost/app/students/K1000000">Base Data</a>'
+            in self.browser.contents)
+        # They do not see all links ...
+        self.assertFalse(
+            '<a href="http://localhost/app/students/K1000000/history">History</a>'
+            in self.browser.contents)
+        # ... and can't change anything
+        self.assertRaises(
+            Unauthorized, self.browser.open, self.edit_personal_path)
+        # If the password has expired, parents are logged out and the
+        # student can login again with the original password
+        delta = timedelta(minutes=11)
+        self.app['students'][self.student_id].parents_password[
+            'timestamp'] = datetime.utcnow() - delta
+        self.app['students'][self.student_id]._p_changed = True
+        self.assertRaises(
+            Unauthorized, self.browser.open, self.student_path)
+        # Parents login is written to log file
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'students.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            'K1000000 - browser.pages.LoginPage - K1000000 - Parents logged in'
+            in logcontent)
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 15605)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 15606)
@@ -905,4 +905,11 @@
             student, 'application_slip')
         targets = []
+        if student.getParentsPassword():
+            targets += [
+                {'url': student_url, 'title': 'Base Data'},
+                {'url': student_url + '/studycourse', 'title': _('Study Course')},
+                {'url': student_url + '/payments', 'title': _('Payments')},
+                ]
+            return targets
         if app_slip:
             targets = [{'url': student_url + '/application_slip',
