source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py @ 6112

Last change on this file since 6112 was 6112, checked in by Henrik Bettermann, 13 years ago

Fix tests.

File size: 12.5 KB
Line 
1##
2## test_authentication.py
3## Login : <uli@pu.smp.net>
4## Started on  Fri Aug 20 08:18:58 2010 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2010 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22import grok
23import shutil
24import tempfile
25import unittest
26from zope.app.testing.functional import FunctionalTestCase
27from zope.authentication.interfaces import IAuthentication
28from zope.component import provideAdapter, getUtility
29from zope.component.hooks import setSite, clearSite
30from zope.interface import verify
31from zope.pluggableauth.interfaces import  IAuthenticatedPrincipalFactory
32from zope.publisher.browser import TestRequest
33from zope.publisher.interfaces import IRequest
34from zope.session.interfaces import ISession
35from zope.site import LocalSiteManager
36from zope.testbrowser.testing import Browser
37from zope.testing import cleanup
38from waeup.sirp.testing import FunctionalLayer
39from waeup.sirp.app import University
40from waeup.sirp.applicants.authentication import (
41    ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
42    ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
43    ApplicantPrincipalInfo, ApplicantPrincipal)
44
45
46class FakeBatch(dict):
47    def getAccessCode(self, id):
48        return self.get(id)
49
50class FakeAccessCode(object):
51    def __init__(self, repr, inv_date=None, disabled=False):
52        self.invalidation_date = inv_date
53        self.representation = repr
54        self.disabled = disabled
55
56class FakeInvAccessCode(object):
57    invalidation_date = True
58
59class FakeApplication(object):
60    def __init__(self, ac=None):
61        self.access_code = ac
62
63class FakeSite(grok.Application, grok.Container):
64    def __init__(self):
65        super(FakeSite, self).__init__()
66        self['applicants'] = {
67            'APP': {
68                'APP-12345': FakeApplication('APP-12345'),
69                'APP-54321': FakeApplication('APP-54321'),
70                'APP-22222': FakeApplication('APP-OTHER'),
71                'APP-44444': FakeApplication(),
72                'APP-55555': FakeApplication('APP-OTHER'),
73                },
74            'JAMB': {
75                'JAMB1': FakeApplication(),
76                'JAMB2': FakeApplication('APP-12345'),
77                'JAMB3': FakeApplication('APP-54321'),
78                'JAMB4': FakeApplication('APP-44444'),
79                },
80            }
81        self['accesscodes'] = {
82            'APP': FakeBatch({
83                    'APP-12345': FakeAccessCode('APP-12345'),
84                    'APP-54321': FakeAccessCode('APP-54321', True),
85                    'APP-11111': FakeAccessCode('APP-11111'),
86                    'APP-22222': FakeAccessCode('APP-22222'),
87                    'APP-33333': FakeAccessCode('APP-33333', True),
88                    'APP-44444': FakeAccessCode('APP-44444', True),
89                    'APP-55555': FakeAccessCode('APP-55555', True),
90                    'APP-66666': FakeAccessCode('APP-66666', True, False),
91                    'APP-77777': FakeAccessCode('APP-77777', False, False),
92                    })
93            }
94
95class AuthenticatorPluginTest(unittest.TestCase):
96
97    def setUp(self):
98        self.app = FakeSite()
99        self.app.setSiteManager(LocalSiteManager(self.app))
100        self.plugin = ApplicantsAuthenticatorPlugin()
101        setSite(self.app)
102        return
103
104    def tearDown(self):
105        clearSite()
106        return
107
108    def test_invalid_credentials(self):
109        result = self.plugin.authenticateCredentials('not-a-dict')
110        assert result is None
111
112        result = self.plugin.authenticateCredentials(
113            dict(accesscode=None, foo='blah'))
114        assert result is None
115
116        result = self.plugin.authenticateCredentials(
117            dict(accesscode='Nonsense',))
118        assert result is None
119
120        # Possible cases, where formal correct authentication
121        # data is not valid:
122        result = self.plugin.authenticateCredentials(
123            dict(accesscode='APP-22222'))
124        assert result is None
125
126        result = self.plugin.authenticateCredentials(
127            dict(accesscode='APP-33333'))
128        assert result is None
129
130        result = self.plugin.authenticateCredentials(
131            dict(accesscode='APP-44444'))
132        assert result is None
133       
134        result = self.plugin.authenticateCredentials(
135            dict(accesscode='APP-55555'))
136        assert result is None
137
138        result = self.plugin.authenticateCredentials(
139            dict(accesscode='APP-66666'))
140        assert result is None
141
142        result = self.plugin.authenticateCredentials(
143            dict(accesscode='APP-77777'))
144        assert result is None
145        return
146
147    def test_valid_credentials(self):
148        """The six different cases where we allow login.
149
150        All other combinations should be forbidden.
151        """
152        result = self.plugin.authenticateCredentials(
153            dict(accesscode='APP-11111'))
154        assert result is not None
155
156        result = self.plugin.authenticateCredentials(
157            dict(accesscode='APP-12345'))
158        assert result is not None
159
160        result = self.plugin.authenticateCredentials(
161            dict(accesscode='APP-54321'))
162        assert result is not None
163
164        # check the `principalInfo` method of authenticator
165        # plugin. This is only here to satisfy the coverage report.
166        assert self.plugin.principalInfo('not-an-id') is None
167        return
168
169session_data = {
170    'zope.pluggableauth.browserplugins': {}
171    }
172
173class FakeSession(dict):
174    def __init__(self, request):
175        pass
176
177    def get(self, key, default=None):
178        return self.__getitem__(key, default)
179   
180    def __getitem__(self, key, default=None):
181        return session_data.get(key, default)
182
183    def __setitem__(self, key, value):
184        session_data[key] = value
185        return
186
187class CredentialsPluginTest(unittest.TestCase):
188
189    def setUp(self):
190        self.request = TestRequest()
191        provideAdapter(FakeSession, (IRequest,), ISession)
192        self.plugin = WAeUPApplicantCredentialsPlugin()
193        self.full_request = TestRequest()
194        session_data['zope.pluggableauth.browserplugins'] = {}
195        return
196
197    def tearDown(self):
198        cleanup.tearDown()
199        return
200
201    def filled_request(self, form_dict):
202        request = TestRequest()
203        for key, value in form_dict.items():
204            request.form[key] = value
205        return request
206
207    def test_extractCredentials_invalid(self):
208        result = self.plugin.extractCredentials('not-a-request')
209        assert result is None
210        return
211
212    def test_extractCredentials_empty(self):
213        result = self.plugin.extractCredentials(self.request)
214        assert result is None
215        return
216
217    def test_extractCredentials_full_set(self):
218        request = self.filled_request({
219                'form.ac_prefix': 'APP',
220                'form.ac_series': '1',
221                'form.ac_number': '1234567890',
222                #'form.jamb_reg_no': 'JAMB_NUMBER',
223                })
224        result = self.plugin.extractCredentials(request)
225        self.assertEqual(result, {'accesscode': 'APP-1-1234567890'})
226        return
227
228    def test_extractCredentials_accesscode_only(self):
229        request = self.filled_request({
230                'form.ac_prefix': 'APP',
231                'form.ac_series': '1',
232                'form.ac_number': '1234567890',
233                })
234        result = self.plugin.extractCredentials(request)
235        self.assertEqual(result, {'accesscode': 'APP-1-1234567890'})
236        return
237
238    def test_extractCredentials_from_empty_session(self):
239        session_data['zope.pluggableauth.browserplugins']['credentials'] = None
240        result = self.plugin.extractCredentials(self.request)
241        assert result is None
242        return
243
244    def test_extractCredentials_from_nonempty_session(self):
245        credentials = ApplicantCredentials('APP-1-12345')
246        session_data['zope.pluggableauth.browserplugins'][
247            'credentials'] = credentials
248        result = self.plugin.extractCredentials(self.request)
249        self.assertEqual(result, {'accesscode': 'APP-1-12345'})
250        return
251
252
253class ApplicantCredentialsTest(unittest.TestCase):
254
255    def setUp(self):
256        self.credentials = ApplicantCredentials('SOME_ACCESSCODE')
257        return
258
259    def tearDown(self):
260        return
261
262    def test_methods(self):
263        self.assertEqual(self.credentials.getAccessCode(), 'SOME_ACCESSCODE')
264        assert self.credentials.getLogin() is None
265        assert self.credentials.getPassword() is None
266        return
267
268class FakePluggableAuth(object):
269    prefix = 'foo'
270
271class PrincipalFactoryTest(unittest.TestCase):
272
273    def setUp(self):
274        self.info = ApplicantPrincipalInfo('APP-1-1234567890')
275        return
276
277    def tearDown(self):
278        pass
279
280    def test_principalFactory_interface(self):
281        verify.verifyClass(IAuthenticatedPrincipalFactory,
282                           AuthenticatedApplicantPrincipalFactory
283                           )
284        return
285
286    def test_principalFactory_create(self):
287        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
288
289        assert factory.info is self.info
290        assert factory.request is None
291        return
292
293    def test_principalFactory_call_w_prefix(self):
294        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
295        principal = factory(FakePluggableAuth())
296
297        assert isinstance(principal, ApplicantPrincipal)
298        self.assertEqual(principal.__repr__(),
299                         "ApplicantPrincipal('foo.APP-1-1234567890')")
300        self.assertEqual(principal.id, 'foo.APP-1-1234567890')
301        return
302
303    def test_principalFactory_call_wo_prefix(self):
304        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
305        fake_auth = FakePluggableAuth()
306        fake_auth.prefix = None
307        principal = factory(fake_auth)
308        self.assertEqual(principal.id, 'APP-1-1234567890')
309        return
310
311class PAUSetupTest(FunctionalTestCase):
312    # Check correct setup of authentication components in the
313    # applicants subpackage.
314
315    # When a university is created, we want by default have our local
316    # authentication components (an authenticator plugin and a
317    # credentials plugin) to be registered with the local PAU. Admins
318    # can remove these components on the fly later-on if they wish.
319   
320    layer = FunctionalLayer
321   
322    def setUp(self):
323        super(PAUSetupTest, self).setUp()
324
325        # Setup a sample site for each test
326        app = University()
327        self.dc_root = tempfile.mkdtemp()
328        app['datacenter'].setStoragePath(self.dc_root)
329
330        # Prepopulate the ZODB...
331        self.getRootFolder()['app'] = app
332        self.app = self.getRootFolder()['app']
333        self.browser = Browser()
334        self.browser.handleErrors = False
335
336    def tearDown(self):
337        super(PAUSetupTest, self).tearDown()
338        shutil.rmtree(self.dc_root)
339
340    def test_extra_auth_plugins_installed(self):
341        # Check whether the auth plugins defined in here are setup
342        # automatically when a university is created
343
344        # Get the PAU responsible for the local site ('app')
345        pau = getUtility(IAuthentication, context=self.app)
346        cred_plugins = pau.getCredentialsPlugins()
347        auth_plugins = pau.getAuthenticatorPlugins()
348        cred_names = [name for name, plugin in cred_plugins]
349        auth_names = [name for name, plugin in auth_plugins]
350       
351        # Make sure our local ApplicantsAuthenticatorPlugin is registered...
352        self.assertTrue('applicants' in auth_names)
353        # Make sure our local WAeUPApplicantCredentialsPlugin is registered...
354        self.assertTrue('applicant_credentials' in cred_names)
355        return
356
357def test_suite():
358    suite = unittest.TestSuite()
359    for testcase in [
360        AuthenticatorPluginTest, CredentialsPluginTest,
361        ApplicantCredentialsTest, PrincipalFactoryTest,
362        PAUSetupTest,
363        ]:
364        suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
365                testcase
366                )
367        )
368    return suite
Note: See TracBrowser for help on using the repository browser.