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

Last change on this file since 6651 was 6470, checked in by Henrik Bettermann, 14 years ago

It should always be workflow state not status. My mistake.

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