Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py	(revision 11971)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py	(revision 11972)
@@ -25,5 +25,6 @@
 from waeup.ikoba.interfaces import MessageFactory as _
 from waeup.ikoba.browser import DEFAULT_IMAGE_PATH
-from waeup.ikoba.utils.helpers import string_from_bytes, file_size
+from waeup.ikoba.utils.helpers import (
+    string_from_bytes, file_size, get_fileformat)
 from waeup.ikoba.browser.layout import (
     default_filedisplay_template,
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_authentication.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_authentication.py	(revision 11972)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_authentication.py	(revision 11972)
@@ -0,0 +1,240 @@
+## $Id: test_authentication.py 9334 2012-10-14 21:02:31Z henrik $
+##
+## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+import unittest
+from datetime import datetime, timedelta
+from zope.authentication.interfaces import IAuthentication
+from zope.component import provideUtility, queryUtility, getGlobalSiteManager
+from zope.interface.verify import verifyClass, verifyObject
+from zope.password.password import SSHAPasswordManager
+from zope.password.interfaces import IPasswordManager
+from zope.pluggableauth import PluggableAuthentication
+from zope.security.interfaces import Unauthorized
+from zope.securitypolicy.role import Role
+from zope.securitypolicy.interfaces import IRole, Allow
+from waeup.ikoba.authentication import get_principal_role_manager
+from waeup.ikoba.interfaces import IAuthPluginUtility, IUserAccount
+from waeup.ikoba.customers.authentication import (
+    CustomersAuthenticatorSetup, CustomerAccount)
+from waeup.ikoba.customers.tests.test_browser import CustomersFullSetup
+from waeup.ikoba.testing import FunctionalLayer
+
+class CustomersAuthenticatorSetupTests(unittest.TestCase):
+
+    def test_iface(self):
+        obj = CustomersAuthenticatorSetup()
+        verifyClass(IAuthPluginUtility, CustomersAuthenticatorSetup)
+        verifyObject(IAuthPluginUtility, obj)
+        return
+
+    def test_register(self):
+        # Make sure registration works.
+        setup = CustomersAuthenticatorSetup()
+        pau = PluggableAuthentication()
+        setup.register(pau)
+        self.assertTrue('customers' in pau.authenticatorPlugins)
+        return
+
+    def test_unregister(self):
+        # Make sure deregistration works.
+        setup = CustomersAuthenticatorSetup()
+        pau = PluggableAuthentication()
+        pau.authenticatorPlugins = ('customers')
+        setup.unregister(pau)
+        self.assertTrue('customers' not in pau.authenticatorPlugins)
+        return
+
+
+class FakeCustomer(object):
+    customer_id = 'test_stud'
+    display_fullname = 'Test User'
+    password = None
+    email = None
+    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
+
+
+class MinimalPAU(PluggableAuthentication):
+    def getPrincipal(self, id):
+        return 'faked principal'
+
+class CustomerAccountTests(unittest.TestCase):
+
+    def setUp(self):
+        self.fake_stud = FakeCustomer()
+        self.account = CustomerAccount(self.fake_stud)
+
+        # We provide a minimal PAU
+        pau = MinimalPAU()
+        provideUtility(pau, IAuthentication)
+
+        # We register a role
+        test_role = Role('waeup.test.Role', 'Testing Role')
+        provideUtility(test_role, IRole, name='waeup.test.Role')
+
+        # We have to setup a password manager utility manually as we
+        # have no functional test. In functional tests this would
+        # happen automatically, but it would take a lot more time to
+        # run the tests.
+        provideUtility(
+            SSHAPasswordManager(), IPasswordManager, 'SSHA')
+        return
+
+    def tearDown(self):
+        self.account.roles = [] # make sure roles are reset
+        gsm = getGlobalSiteManager()
+        to_clean = []
+        # Clear up utilities registered in setUp
+        to_clean.append(
+            (IPasswordManager, queryUtility(
+                    IPasswordManager, name='SSHA', default=None)))
+        to_clean.append(
+            (IAuthentication, queryUtility(IAuthentication, default=None)))
+        to_clean.append(
+            (IRole, queryUtility(IRole, name='test.Role', default=None)))
+        for iface, elem in to_clean:
+            if elem is not None:
+                gsm.unregisterUtility(elem, iface)
+        return
+
+    def test_iface(self):
+        verifyClass(IUserAccount, CustomerAccount)
+        verifyObject(IUserAccount, self.account)
+        return
+
+    def test_set_password(self):
+        # make sure we can set a password.
+        self.account.setPassword('secret')
+        self.assertTrue(self.fake_stud.password is not None)
+        # we do not store plaintext passwords
+        self.assertTrue(self.fake_stud.password != 'secret')
+        # passwords are stored as bytestreams
+        self.assertTrue(isinstance(self.fake_stud.password, basestring))
+        self.assertFalse(isinstance(self.fake_stud.password, unicode))
+        return
+
+    def test_check_password(self):
+        # make sure we can check a password.
+        self.account.setPassword('secret')
+        result1 = self.account.checkPassword(None)
+        result2 = self.account.checkPassword('nonsense')
+        result3 = self.account.checkPassword('secret')
+        self.assertEqual(result1, False)
+        self.assertEqual(result2, False)
+        self.assertEqual(result3, True)
+        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
+        self.fake_stud.password = None
+        result1 = self.account.checkPassword('')
+        self.fake_stud.password = ''
+        result2 = self.account.checkPassword('')
+        self.assertEqual(result1, False)
+        self.assertEqual(result2, False)
+        return
+
+    def test_check_password_no_string(self):
+        # if passed in password is not a string, we gain no access
+        self.fake_stud.password = 'secret'
+        result1 = self.account.checkPassword(None)
+        result2 = self.account.checkPassword(object())
+        self.assertEqual(result1, False)
+        self.assertEqual(result2, False)
+        return
+
+    def test_role_set(self):
+        # make sure we can set roles for principals denoted by account
+        prm = get_principal_role_manager()
+        self.assertEqual(prm.getPrincipalsAndRoles(), [])
+        self.account.roles = ['waeup.test.Role']
+        self.assertEqual(
+            prm.getPrincipalsAndRoles(),
+            [('waeup.test.Role', 'test_stud', Allow)])
+        return
+
+    def test_role_get(self):
+        # make sure we can get roles set for an account
+        self.assertEqual(self.account.roles, [])
+        self.account.roles = ['waeup.test.Role',] # set a role
+        self.assertEqual(self.account.roles, ['waeup.test.Role'])
+        return
+
+
+
+class FunctionalCustomerAuthTests(CustomersFullSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(FunctionalCustomerAuthTests, self).setUp()
+        return
+
+    def tearDown(self):
+        super(FunctionalCustomerAuthTests, self).tearDown()
+        return
+
+    def test_reset_protected_anonymous(self):
+        # anonymous users cannot reset others passwords
+        self.assertRaises(
+            Unauthorized,
+            self.browser.open, self.customer_path + '/change_password')
+        return
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 11971)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py	(revision 11972)
@@ -323,86 +323,7 @@
         return
 
-    def xxx_test_manage_upload_file(self):
-        # Managers can upload a file via the CustomerClearanceManageFormPage
-        # The image is stored even if form has errors
-        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
-        self.browser.open(self.manage_clearance_path)
-        # No birth certificate has been uploaded yet
-        # Browsing the link shows a placerholder image
-        self.browser.open('birth_certificate')
-        self.assertEqual(
-            self.browser.headers['content-type'], 'image/jpeg')
-        self.assertEqual(len(self.browser.contents), PH_LEN)
-        # Create a pseudo image file and select it to be uploaded in form
-        # as birth certificate
-        self.browser.open(self.manage_clearance_path)
-        image = open(SAMPLE_IMAGE, 'rb')
-        ctrl = self.browser.getControl(name='birthcertificateupload')
-        file_ctrl = ctrl.mech_control
-        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
-        # The Save action does not upload files
-        self.browser.getControl("Save").click() # submit form
-        self.assertFalse(
-            '<a target="image" href="birth_certificate">'
-            in self.browser.contents)
-        # ... but the correct upload submit button does
-        image = open(SAMPLE_IMAGE)
-        ctrl = self.browser.getControl(name='birthcertificateupload')
-        file_ctrl = ctrl.mech_control
-        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
-        self.browser.getControl(
-            name='upload_birthcertificateupload').click()
-        # There is a correct <img> link included
-        self.assertTrue(
-            'href="http://localhost/app/customers/K1000000/birth_certificate"'
-            in self.browser.contents)
-        # Browsing the link shows a real image
-        self.browser.open('birth_certificate')
-        self.assertEqual(
-            self.browser.headers['content-type'], 'image/jpeg')
-        self.assertEqual(len(self.browser.contents), 2787)
-        # We can't reupload a file. The existing file must be deleted first.
-        self.browser.open(self.manage_clearance_path)
-        self.assertFalse(
-            'upload_birthcertificateupload' in self.browser.contents)
-        # File must be deleted first
-        self.browser.getControl(name='delete_birthcertificateupload').click()
-        self.assertTrue(
-            'birth_certificate deleted' in self.browser.contents)
-        # Uploading a file which is bigger than 150k will raise an error
-        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
-        ctrl = self.browser.getControl(name='birthcertificateupload')
-        file_ctrl = ctrl.mech_control
-        file_ctrl.add_file(big_image, filename='my_birth_certificate.jpg')
-        self.browser.getControl(
-            name='upload_birthcertificateupload').click()
-        self.assertTrue(
-            'Uploaded file is too big' in self.browser.contents)
-        # we do not rely on filename extensions given by uploaders
-        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
-        ctrl = self.browser.getControl(name='birthcertificateupload')
-        file_ctrl = ctrl.mech_control
-        # tell uploaded file is bmp
-        file_ctrl.add_file(image, filename='my_birth_certificate.bmp')
-        self.browser.getControl(
-            name='upload_birthcertificateupload').click()
-        self.assertTrue(
-            # jpg file was recognized
-            'File birth_certificate.jpg uploaded.' in self.browser.contents)
-        # Delete file again
-        self.browser.getControl(name='delete_birthcertificateupload').click()
-        self.assertTrue(
-            'birth_certificate deleted' in self.browser.contents)
-        # File names must meet several conditions
-        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
-        ctrl = self.browser.getControl(name='birthcertificateupload')
-        file_ctrl = ctrl.mech_control
-        file_ctrl.add_file(bmp_image, filename='my_birth_certificate.bmp')
-        self.browser.getControl(
-            name='upload_birthcertificateupload').click()
-        self.assertTrue('Only the following extensions are allowed'
-            in self.browser.contents)
-
+    def test_manage_upload_passport(self):
         # Managers can upload a file via the CustomerBaseManageFormPage
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.browser.open(self.manage_customer_path)
         image = open(SAMPLE_IMAGE_BMP, 'rb')
@@ -426,8 +347,6 @@
         self.browser.open(self.manage_customer_path)
         self.browser.getControl('Delete').click()
-        self.browser.open(self.customer_path + '/clearance_slip.pdf')
-        self.assertEqual(self.browser.headers['Status'], '200 Ok')
-        self.assertEqual(self.browser.headers['Content-Type'],
-                         'application/pdf')
+        self.assertTrue('passport.jpg deleted' in self.browser.contents)
+
 
     def test_manage_workflow(self):
@@ -440,4 +359,8 @@
         self.browser.getControl(name="transition").value = ['request']
         self.browser.getControl("Save").click()
+        self.browser.getControl(name="transition").value = ['reject']
+        self.browser.getControl("Save").click()
+        self.browser.getControl(name="transition").value = ['request']
+        self.browser.getControl("Save").click()
         self.browser.getControl(name="transition").value = ['approve']
         self.browser.getControl("Save").click()
