Ignore:
Timestamp:
30 Nov 2011, 23:13:26 (13 years ago)
Author:
Henrik Bettermann
Message:

Rebuild applicants package (1st part). Applicants now have an applicant_id and a password and can use the regular login page to enter the portal.

Add user_type attribute to SIRPPrincipal objects.

Add some permissions in students package.

Some tests are still missing and will be re-added soon.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py

    r7238 r7240  
    1616## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    1717##
    18 import shutil
    19 import tempfile
    2018import unittest
    2119from 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
     20from zope.component import provideUtility, queryUtility, getGlobalSiteManager
     21from zope.interface.verify import verifyClass, verifyObject
     22from zope.password.password import SSHAPasswordManager
     23from zope.password.interfaces import IPasswordManager
     24from zope.pluggableauth import PluggableAuthentication
     25from zope.security.interfaces import Unauthorized
     26from zope.securitypolicy.role import Role
     27from zope.securitypolicy.interfaces import IRole, Allow
     28from waeup.sirp.authentication import get_principal_role_manager
     29from waeup.sirp.interfaces import IAuthPluginUtility, IUserAccount
    3530from waeup.sirp.applicants.authentication import (
    36     ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
    37     ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
    38     ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthenticatorSetup)
     31    ApplicantsAuthenticatorSetup, ApplicantAccount)
     32from waeup.sirp.applicants.tests.test_browser import ApplicantsFullSetup
     33from waeup.sirp.testing import FunctionalLayer
     34
     35class 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
    3959
    4060
    41 class FakeBatch(dict):
    42     def getAccessCode(self, id):
    43         return self.get(id)
     61class FakeApplicant(object):
     62    applicant_id = 'test_appl'
     63    fullname = 'Tilman Gause'
     64    password = None
     65    email = None
     66    phone = None
    4467
    45 class FakeAccessCode(object):
    46     def __init__(self, repr, state = 'initialized'):
    47         self.representation = repr
    48         self.state = state
    4968
    50 class FakeApplication(object):
    51     def __init__(self, ac=None):
    52         self.access_code = ac
     69class MinimalPAU(PluggableAuthentication):
     70    def getPrincipal(self, id):
     71        return 'faked principal'
    5372
    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
     73class ApplicantAccountTests(unittest.TestCase):
    9674
    9775    def setUp(self):
    98         super(AuthenticatorPluginTest, self).setUp()
     76        self.fake_stud = FakeApplicant()
     77        self.account = ApplicantAccount(self.fake_stud)
    9978
    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)
    10482
    105         # Prepopulate the ZODB...
    106         self.getRootFolder()['app'] = app
    107         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')
    10886
    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')
    11793        return
    11894
    11995    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)
    122110        return
    123111
    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)
    154115        return
    155116
    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))
    176125        return
    177126
    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)
    194136        return
    195137
    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         session_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)
    204146        return
    205147
    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)
    208155        return
    209156
    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)])
    219165        return
    220166
    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'])
    224172        return
    225173
    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.
     174class FunctionalApplicantAuthTests(ApplicantsFullSetup):
    328175
    329176    layer = FunctionalLayer
    330177
    331178    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
    344181
    345182    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()
    364184        return
    365 
    366 class FakePAU(object):
    367     credentialsPlugins = ()
    368     authenticatorPlugins = ()
    369 
    370 class ApplicantsAuthenticatorSetupTests(FunctionalTestCase):
    371 
    372     layer = FunctionalLayer
    373 
    374     def test_ifaces(self):
    375         # make sure we fullfill the interface promises
    376         obj = ApplicantsAuthenticatorSetup()
    377         verify.verifyClass(IAuthPluginUtility, ApplicantsAuthenticatorSetup)
    378         verify.verifyObject(IAuthPluginUtility, obj)
    379         return
    380 
    381     def test_utility_available(self):
    382         # we can get an applicant auth utility by lookup
    383         util = queryUtility(IAuthPluginUtility,
    384                             name='applicants_auth_setup')
    385         self.assertTrue(util is not None)
    386         return
    387 
    388     def test_register(self):
    389         # make sure we can register additional components
    390         pau = FakePAU()
    391         result = ApplicantsAuthenticatorSetup().register(pau)
    392         self.assertEqual(
    393             result.credentialsPlugins, ('applicant_credentials',))
    394         self.assertEqual(
    395             result.authenticatorPlugins, ('applicants',))
    396         return
    397 
    398     def test_unregister(self):
    399         # make sure we can unregister applicant auth components
    400         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
Note: See TracChangeset for help on using the changeset viewer.