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

Last change on this file since 7060 was 6730, checked in by uli, 13 years ago

Use logger-aware w.s.testing components.
Remove unused imports.

File size: 12.9 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 shutil
23import tempfile
24import unittest
25from zope.authentication.interfaces import IAuthentication
26from zope.component import provideAdapter, getUtility
27from zope.component.hooks import setSite
28from zope.interface import verify
29from zope.pluggableauth.interfaces import  IAuthenticatedPrincipalFactory
30from zope.publisher.browser import TestRequest
31from zope.publisher.interfaces import IRequest
32from zope.session.interfaces import ISession
33from zope.testbrowser.testing import Browser
34from zope.testing import cleanup
35from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
36from waeup.sirp.app import University
37from waeup.sirp.applicants import  ApplicantsContainer, Applicant
38from waeup.sirp.applicants.authentication import (
39    ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
40    ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
41    ApplicantPrincipalInfo, ApplicantPrincipal,)
42
43
44class FakeBatch(dict):
45    def getAccessCode(self, id):
46        return self.get(id)
47
48class FakeAccessCode(object):
49    def __init__(self, repr, state = 'initialized'):
50        self.representation = repr
51        self.state = state
52
53class FakeApplication(object):
54    def __init__(self, ac=None):
55        self.access_code = ac
56
57def FakeSite():
58    return {
59        'applicants': {
60            'APP': {
61                'APP-12345': FakeApplication(u'APP-12345'),
62                'APP-54321': FakeApplication(u'APP-54321'),
63                'APP-22222': FakeApplication(u'APP-OTHER'),
64                'APP-44444': FakeApplication(),
65                'APP-55555': FakeApplication(u'APP-OTHER'),
66                'APP-77777': FakeApplication(u'APP-77777'),
67                },
68            },
69        'accesscodes': {
70            'APP': FakeBatch({
71                    'APP-12345': FakeAccessCode('APP-12345'),
72                    'APP-54321': FakeAccessCode('APP-54321', 'used'),
73                    'APP-11111': FakeAccessCode('APP-11111'),
74                    'APP-22222': FakeAccessCode('APP-22222'),
75                    'APP-33333': FakeAccessCode('APP-33333', 'used'),
76                    'APP-44444': FakeAccessCode('APP-44444', 'used'),
77                    'APP-55555': FakeAccessCode('APP-55555', 'used'),
78                    'APP-66666': FakeAccessCode('APP-66666', 'disabled'),
79                    'APP-77777': FakeAccessCode('APP-77777', 'disabled'),
80                    })
81            }
82        }
83
84class AuthenticatorPluginTest(FunctionalTestCase):
85
86    layer = FunctionalLayer
87
88    def create_applicant(self, cname, aname, ac=None):
89        # Create an applicant in an applicants container
90        setSite(self.app)
91        if not cname in self.app['applicants'].keys():
92            container = ApplicantsContainer()
93            self.app['applicants'][cname] = container
94        applicant = Applicant()
95        if ac is not None:
96            applicant.access_code = ac
97        self.app['applicants'][cname][aname] = applicant
98        return
99
100    def setUp(self):
101        super(AuthenticatorPluginTest, self).setUp()
102
103        # Setup a sample site for each test
104        app = University()
105        self.dc_root = tempfile.mkdtemp()
106        app['datacenter'].setStoragePath(self.dc_root)
107
108        # Prepopulate the ZODB...
109        self.getRootFolder()['app'] = app
110        self.app = self.getRootFolder()['app']
111
112        fake_site = FakeSite()
113        for ckey, fake_container in fake_site['applicants'].items():
114            for akey, fake_appl in fake_container.items():
115                self.create_applicant(ckey, akey, fake_appl.access_code)
116        del self.app['accesscodes']
117        self.app['accesscodes'] = fake_site['accesscodes']
118
119        self.plugin = ApplicantsAuthenticatorPlugin()
120        return
121
122    def tearDown(self):
123        super(AuthenticatorPluginTest, self).tearDown()
124        shutil.rmtree(self.dc_root)
125        return
126
127    def test_invalid_credentials(self):
128        result = self.plugin.authenticateCredentials('not-a-dict')
129        assert result is None
130
131        result = self.plugin.authenticateCredentials(
132            dict(accesscode=None, foo='blah'))
133        assert result is None
134
135        result = self.plugin.authenticateCredentials(
136            dict(accesscode='Nonsense',))
137        assert result is None
138
139        # Possible cases, where formal correct authentication
140        # data is not valid:
141        result = self.plugin.authenticateCredentials(
142            dict(accesscode='APP-33333'))
143        assert result is None
144
145        result = self.plugin.authenticateCredentials(
146            dict(accesscode='APP-55555'))
147        assert result is None
148
149        result = self.plugin.authenticateCredentials(
150            dict(accesscode='APP-66666'))
151        assert result is None
152
153        result = self.plugin.authenticateCredentials(
154            dict(accesscode='APP-77777'))
155        assert result is None
156
157        return
158
159    def test_valid_credentials(self):
160        """The six different cases where we allow login.
161
162        All other combinations should be forbidden.
163        """
164        result = self.plugin.authenticateCredentials(
165            dict(accesscode='APP-11111'))
166        assert result is not None
167
168        result = self.plugin.authenticateCredentials(
169            dict(accesscode='APP-12345'))
170        assert result is not None
171
172        result = self.plugin.authenticateCredentials(
173            dict(accesscode='APP-54321'))
174        assert result is not None
175
176        # check the `principalInfo` method of authenticator
177        # plugin. This is only here to satisfy the coverage report.
178        assert self.plugin.principalInfo('not-an-id') is None
179        return
180
181session_data = {
182    'zope.pluggableauth.browserplugins': {}
183    }
184
185class FakeSession(dict):
186    def __init__(self, request):
187        pass
188
189    def get(self, key, default=None):
190        return self.__getitem__(key, default)
191
192    def __getitem__(self, key, default=None):
193        return session_data.get(key, default)
194
195    def __setitem__(self, key, value):
196        session_data[key] = value
197        return
198
199class CredentialsPluginTest(unittest.TestCase):
200
201    def setUp(self):
202        self.request = TestRequest()
203        provideAdapter(FakeSession, (IRequest,), ISession)
204        self.plugin = WAeUPApplicantCredentialsPlugin()
205        self.full_request = TestRequest()
206        session_data['zope.pluggableauth.browserplugins'] = {}
207        return
208
209    def tearDown(self):
210        cleanup.tearDown()
211        return
212
213    def filled_request(self, form_dict):
214        request = TestRequest()
215        for key, value in form_dict.items():
216            request.form[key] = value
217        return request
218
219    def test_extractCredentials_invalid(self):
220        result = self.plugin.extractCredentials('not-a-request')
221        assert result is None
222        return
223
224    def test_extractCredentials_empty(self):
225        result = self.plugin.extractCredentials(self.request)
226        assert result is None
227        return
228
229    def test_extractCredentials_full_set(self):
230        request = self.filled_request({
231                'form.ac_prefix': 'APP',
232                'form.ac_series': '1',
233                'form.ac_number': '1234567890',
234                #'form.jamb_reg_no': 'JAMB_NUMBER',
235                })
236        result = self.plugin.extractCredentials(request)
237        self.assertEqual(result, {'accesscode': 'APP-1-1234567890'})
238        return
239
240    def test_extractCredentials_accesscode_only(self):
241        request = self.filled_request({
242                'form.ac_prefix': 'APP',
243                'form.ac_series': '1',
244                'form.ac_number': '1234567890',
245                })
246        result = self.plugin.extractCredentials(request)
247        self.assertEqual(result, {'accesscode': 'APP-1-1234567890'})
248        return
249
250    def test_extractCredentials_from_empty_session(self):
251        session_data['zope.pluggableauth.browserplugins']['credentials'] = None
252        result = self.plugin.extractCredentials(self.request)
253        assert result is None
254        return
255
256    def test_extractCredentials_from_nonempty_session(self):
257        credentials = ApplicantCredentials('APP-1-12345')
258        session_data['zope.pluggableauth.browserplugins'][
259            'credentials'] = credentials
260        result = self.plugin.extractCredentials(self.request)
261        self.assertEqual(result, {'accesscode': 'APP-1-12345'})
262        return
263
264
265class ApplicantCredentialsTest(unittest.TestCase):
266
267    def setUp(self):
268        self.credentials = ApplicantCredentials('SOME_ACCESSCODE')
269        return
270
271    def tearDown(self):
272        return
273
274    def test_methods(self):
275        self.assertEqual(self.credentials.getAccessCode(), 'SOME_ACCESSCODE')
276        assert self.credentials.getLogin() is None
277        assert self.credentials.getPassword() is None
278        return
279
280class FakePluggableAuth(object):
281    prefix = 'foo'
282
283class PrincipalFactoryTest(unittest.TestCase):
284
285    def setUp(self):
286        self.info = ApplicantPrincipalInfo('APP-1-1234567890')
287        return
288
289    def tearDown(self):
290        pass
291
292    def test_principalFactory_interface(self):
293        verify.verifyClass(IAuthenticatedPrincipalFactory,
294                           AuthenticatedApplicantPrincipalFactory
295                           )
296        return
297
298    def test_principalFactory_create(self):
299        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
300
301        assert factory.info is self.info
302        assert factory.request is None
303        return
304
305    def test_principalFactory_call_w_prefix(self):
306        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
307        principal = factory(FakePluggableAuth())
308
309        assert isinstance(principal, ApplicantPrincipal)
310        self.assertEqual(principal.__repr__(),
311                         "ApplicantPrincipal('foo.APP-1-1234567890')")
312        self.assertEqual(principal.id, 'foo.APP-1-1234567890')
313        return
314
315    def test_principalFactory_call_wo_prefix(self):
316        factory = AuthenticatedApplicantPrincipalFactory(self.info, None)
317        fake_auth = FakePluggableAuth()
318        fake_auth.prefix = None
319        principal = factory(fake_auth)
320        self.assertEqual(principal.id, 'APP-1-1234567890')
321        return
322
323class PAUSetupTest(FunctionalTestCase):
324    # Check correct setup of authentication components in the
325    # applicants subpackage.
326
327    # When a university is created, we want by default have our local
328    # authentication components (an authenticator plugin and a
329    # credentials plugin) to be registered with the local PAU. Admins
330    # can remove these components on the fly later-on if they wish.
331
332    layer = FunctionalLayer
333
334    def setUp(self):
335        super(PAUSetupTest, self).setUp()
336
337        # Setup a sample site for each test
338        app = University()
339        self.dc_root = tempfile.mkdtemp()
340        app['datacenter'].setStoragePath(self.dc_root)
341
342        # Prepopulate the ZODB...
343        self.getRootFolder()['app'] = app
344        self.app = self.getRootFolder()['app']
345        self.browser = Browser()
346        self.browser.handleErrors = False
347
348    def tearDown(self):
349        super(PAUSetupTest, self).tearDown()
350        shutil.rmtree(self.dc_root)
351
352    def test_extra_auth_plugins_installed(self):
353        # Check whether the auth plugins defined in here are setup
354        # automatically when a university is created
355
356        # Get the PAU responsible for the local site ('app')
357        pau = getUtility(IAuthentication, context=self.app)
358        cred_plugins = pau.getCredentialsPlugins()
359        auth_plugins = pau.getAuthenticatorPlugins()
360        cred_names = [name for name, plugin in cred_plugins]
361        auth_names = [name for name, plugin in auth_plugins]
362
363        # Make sure our local ApplicantsAuthenticatorPlugin is registered...
364        self.assertTrue('applicants' in auth_names)
365        # Make sure our local WAeUPApplicantCredentialsPlugin is registered...
366        self.assertTrue('applicant_credentials' in cred_names)
367        return
368
369def test_suite():
370    suite = unittest.TestSuite()
371    for testcase in [
372        AuthenticatorPluginTest, CredentialsPluginTest,
373        ApplicantCredentialsTest, PrincipalFactoryTest,
374        PAUSetupTest,
375        ]:
376        suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
377                testcase
378                )
379        )
380    return suite
Note: See TracBrowser for help on using the repository browser.