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

Last change on this file since 7119 was 7063, checked in by uli, 13 years ago

Merge changes from branch ulif-extimgstore back into trunk.
Beside external image storage also waeupdocs should work again.

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