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

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

More copyright adjustments.

  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1## $Id: test_authentication.py 7193 2011-11-25 07:21:29Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import shutil
19import tempfile
20import unittest
21from zope.authentication.interfaces import IAuthentication
22from zope.component import provideAdapter, getUtility, queryUtility
23from zope.component.hooks import setSite
24from zope.interface import verify
25from zope.pluggableauth.interfaces import  IAuthenticatedPrincipalFactory
26from zope.publisher.browser import TestRequest
27from zope.publisher.interfaces import IRequest
28from zope.session.interfaces import ISession
29from zope.testbrowser.testing import Browser
30from zope.testing import cleanup
31from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
32from waeup.sirp.app import University
33from waeup.sirp.interfaces import IAuthPluginUtility
34from waeup.sirp.applicants import  ApplicantsContainer, Applicant
35from waeup.sirp.applicants.authentication import (
36    ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
37    ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
38    ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthUtility)
39
40
41class FakeBatch(dict):
42    def getAccessCode(self, id):
43        return self.get(id)
44
45class FakeAccessCode(object):
46    def __init__(self, repr, state = 'initialized'):
47        self.representation = repr
48        self.state = state
49
50class FakeApplication(object):
51    def __init__(self, ac=None):
52        self.access_code = ac
53
54def 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
81class 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
96
97    def setUp(self):
98        super(AuthenticatorPluginTest, self).setUp()
99
100        # Setup a sample site for each test
101        app = University()
102        self.dc_root = tempfile.mkdtemp()
103        app['datacenter'].setStoragePath(self.dc_root)
104
105        # Prepopulate the ZODB...
106        self.getRootFolder()['app'] = app
107        self.app = self.getRootFolder()['app']
108
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()
117        return
118
119    def tearDown(self):
120        super(AuthenticatorPluginTest, self).tearDown()
121        shutil.rmtree(self.dc_root)
122        return
123
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
154        return
155
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
176        return
177
178session_data = {
179    'zope.pluggableauth.browserplugins': {}
180    }
181
182class 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
194        return
195
196class 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'] = {}
204        return
205
206    def tearDown(self):
207        cleanup.tearDown()
208        return
209
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
219        return
220
221    def test_extractCredentials_empty(self):
222        result = self.plugin.extractCredentials(self.request)
223        assert result is None
224        return
225
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
262class 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
277class FakePluggableAuth(object):
278    prefix = 'foo'
279
280class 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
320class 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.
328
329    layer = FunctionalLayer
330
331    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
344
345    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)
364        return
365
366class FakePAU(object):
367    credentialsPlugins = ()
368    authenticatorPlugins = ()
369
370class ApplicantsAuthUtilityTests(FunctionalTestCase):
371
372    layer = FunctionalLayer
373
374    def test_ifaces(self):
375        # make sure we fullfill the interface promises
376        obj = ApplicantsAuthUtility()
377        verify.verifyClass(IAuthPluginUtility, ApplicantsAuthUtility)
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 = ApplicantsAuthUtility().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 = ApplicantsAuthUtility()
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 TracBrowser for help on using the repository browser.