source: main/waeup.cas/trunk/waeup/cas/tests/test_authenticators.py @ 14086

Last change on this file since 14086 was 10535, checked in by uli, 11 years ago

pep8.

File size: 15.5 KB
Line 
1# Tests for waeup.cas.authentictors
2import os
3import threading
4import unittest
5from paste.deploy import loadapp
6try:
7    from SimpleXMLRPCServer import SimpleXMLRPCServer  # Python 2.x
8except ImportError:
9    from xmlrpc.server import SimpleXMLRPCServer       # Python 3.x
10try:
11    import xmlrpclib                     # Python 2.x
12except ImportError:
13    import xmlrpc.client as xmlrpclib    # Python 3.x
14from waeup.cas.authenticators import (
15    get_all_authenticators, get_authenticator, filter_auth_opts,
16    Authenticator, DummyAuthenticator, KofaAuthenticator,
17    KofaMoodleAuthenticator
18    )
19from waeup.cas.server import CASServer
20
21
22class TestHelpers(unittest.TestCase):
23
24    def test_get_all_authenticators(self):
25        # we can get authenticators via entry points
26        auths = get_all_authenticators()
27        assert 'dummy' in auths
28        assert auths['dummy'] is DummyAuthenticator
29
30    def test_filter_auth_opts(self):
31        # we can filter auth opts
32        result = filter_auth_opts(
33            dict(auth='foo', auth_bar='baz', auth_baz='blah')
34            )
35        assert result == (
36            {'auth': 'foo'},
37            {'auth_bar': 'baz', 'auth_baz': 'blah'}
38            )
39
40    def test_get_authenticator(self):
41        # we can parse a paste.deploy config dict for auth
42        result = get_authenticator({})
43        assert result == {}
44        result = get_authenticator({'auth': 'dummy'})
45        assert isinstance(result['auth'], DummyAuthenticator)
46
47
48class AuthenticatorTests(unittest.TestCase):
49
50    def test_create(self):
51        # we can create Authenticator instances
52        auth = Authenticator()
53        assert isinstance(auth, Authenticator)
54
55
56class DummyAuthenticatorTests(unittest.TestCase):
57
58    def test_create(self):
59        # we can create DummyAuthenticator instances
60        auth = DummyAuthenticator()
61        assert isinstance(auth, DummyAuthenticator)
62
63    def test_check_credentials(self):
64        # we can succeed with 'bird'/'bebop'. Others will fail.
65        auth = DummyAuthenticator()
66        result1 = auth.check_credentials('bird', 'bebop')
67        assert result1 == (True, '')
68        result2 = auth.check_credentials('foo', 'bar')
69        assert result2 == (False, 'Invalid username or password.')
70
71
72BACKENDS1 = dict(
73    inst1=dict(
74        url='http://localhost:6666/app',
75        marker='^MA-',
76        )
77    )
78
79BACKENDS2 = dict(
80    inst1=dict(
81        url='http://localhost:6666/',
82        marker='^SCHOOL1-',
83        )
84    )
85
86BACKENDS3 = dict(
87    inst1=dict(
88        url='http://localhost:6666/',
89        marker='^SCHOOL1-',
90        moodle_url='http://localhost:7777/',
91        )
92    )
93
94
95class KofaAuthenticatorTests(unittest.TestCase):
96
97    def test_create(self):
98        # we can create KofaAuthenticator instances
99        auth = KofaAuthenticator()
100        assert isinstance(auth, KofaAuthenticator)
101
102    def test_options_defaults(self):
103        # all options have sensible defaults
104        auth = KofaAuthenticator()
105        assert auth.backends == {}
106
107    def test_options(self):
108        # we can pass options
109        auth = KofaAuthenticator(auth_backends=str(BACKENDS1))
110        assert auth.backends == BACKENDS1
111        auth = KofaAuthenticator(auth_backends='{"foo": {"url": "bar"}}')
112        assert auth.backends['foo']['marker'] == '.+'
113        self.assertRaises(
114            ValueError, KofaAuthenticator, auth_backends='not-a-py-expr')
115        self.assertRaises(
116            ValueError, KofaAuthenticator, auth_backends='"Not a dict"')
117        self.assertRaises(
118            ValueError, KofaAuthenticator,
119            auth_backends='{"foo": "not-a-dict"}')
120        self.assertRaises(
121            ValueError, KofaAuthenticator,
122            auth_backends='{"foo": {"no-url-key": "bar"}}')
123        self.assertRaises(
124            ValueError, KofaAuthenticator,
125            auth_backends='{"foo": {"url": "bar", "marker": "inv_re)"}}')
126
127    def test_paste_deploy_options(self):
128        # we can set CAS server-related options via paste.deploy config
129        paste_conf = os.path.join(
130            os.path.dirname(__file__), 'sample3.ini')
131        app = loadapp('config:%s' % paste_conf)
132        assert isinstance(app, CASServer)
133        assert app.db_connection_string == 'sqlite:///:memory:'
134        assert isinstance(app.auth, KofaAuthenticator)
135
136
137class KofaMoodleAuthenticatorTests(unittest.TestCase):
138
139    def test_paste_deploy_options(self):
140        # we can set CAS server-related options via paste.deploy config
141        paste_conf = os.path.join(
142            os.path.dirname(__file__), 'sample4.ini')
143        app = loadapp('config:%s' % paste_conf)
144        assert isinstance(app, CASServer)
145        assert app.db_connection_string == 'sqlite:///:memory:'
146        assert isinstance(app.auth, KofaAuthenticator)
147        assert app.auth.name == 'kofa_moodle1'
148        auth_backends = {
149          'kofa_moodle1':
150            {'url': 'http://xmlrpcuser1:xmlrpcuser1@localhost:8080/app1/',
151             'marker': '^K',
152             'moodle_url': ('http://localhost/moodle/webservice/xmlrpc'
153                            '/server.php?wstoken=de1e1cbacf91ec6290bb6'
154                            'f6339122e7d'),
155            },
156          }
157        assert app.auth.backends == auth_backends
158
159
160class FakeKofaServer(SimpleXMLRPCServer):
161    # A fake Kofa server that provides only XMLRPC methods
162
163    allow_reuse_address = True
164
165    def __init__(self, *args, **kw):
166        kw.update(allow_none=True)
167        SimpleXMLRPCServer.__init__(self, *args, **kw)
168        self.register_function(
169            self._check_credentials, 'check_applicant_credentials')
170        self.register_function(
171            self._check_credentials, 'check_student_credentials')
172        self.register_function(
173            self._get_moodle_data, 'get_student_moodle_data')
174        self.register_function(
175            self._get_moodle_data, 'get_applicant_moodle_data')
176
177    def _check_credentials(self, username, password):
178        # fake waeup.kofa check_credentials method.
179        #
180        # This method is supposed to mimic the behaviour of an
181        # original waeup.kofa check_credentials method. It returns a
182        # positive result for the credentials `bird`/`bebop`.
183        if username == 'bird' and password == 'bebop':
184            return {'id': 'bird', 'email': 'bird@gods.net',
185                    'description': 'Mr. Charles Parker',
186                    'type': 'student'}
187        if username == 'pig' and password == 'pog':
188            return {'id': 'pig', 'email': 'pig@gods.net',
189                    'description': 'Mr. Ray Charles',
190                    'type': 'applicant'}
191        if username in (
192            'fault1', 'fault2',
193            'fault3', 'fault4', 'fault7') and password == 'biz':
194            return {'type': 'student'}
195        if username == 'fault5' and password == 'biz':
196            return {'type': 'applicant'}
197        if username == 'fault6' and password == 'biz':
198            return {'type': 'boss'}
199        return None
200
201    def _get_moodle_data(self, username):
202        # fake waeup.kofa get_student_moodle_data method.
203        if username == 'bird':
204            return dict(email='aa@aa.aa',
205                        firstname='Charles',
206                        lastname='Parker',
207                        )
208        if username == 'pig':
209            return dict(email='bb@bb.bb',
210                        firstname='Ray',
211                        lastname='Charles',
212                        )
213        if username in ('fault1', 'fault2', 'fault3',
214                        'fault4', 'fault5', 'fault7'):
215            return dict(email='ff@ff.ff',
216                        firstname='John',
217                        lastname='Fault',
218                        )
219
220
221class FakeMoodleServer(SimpleXMLRPCServer):
222    # A fake Moodle server that provides only XMLRPC methods
223
224    allow_reuse_address = True
225
226    def __init__(self, *args, **kw):
227        kw.update(allow_none=True)
228        SimpleXMLRPCServer.__init__(self, *args, **kw)
229        self.register_function(
230            self._core_user_create_users, 'core_user_create_users')
231        self.register_function(
232            self._core_user_get_users, 'core_user_get_users')
233        self.register_function(
234            self._core_user_update_users, 'core_user_update_users')
235
236    def _core_user_create_users(self, arg):
237        # fake Moodle core_user_create_users method.
238        if arg[0]['username'] == 'school1-fault1':
239            raise xmlrpclib.Fault('faultCode', 'core_user_create_users failed')
240        if arg[0]['username'] == 'school1-fault7':
241            raise xmlrpclib.Fault('faultCode', 'Email address already exists')
242        return [{'username': 'any name', 'id': 37}]
243
244    def _core_user_get_users(self, arg):
245        # fake Moodle core_user_get_users method.
246        if arg[0]['value'] == 'SCHOOL1-fault2':
247            raise xmlrpclib.Fault('faultCode', 'core_user_get_users failed')
248        if arg[0]['value'] == 'SCHOOL1-fault3':
249            return {'users': [{'id': 'SCHOOL1-fault3'}]}
250        if arg[0]['value'] in ('SCHOOL1-fault4', 'SCHOOL1-fault5'):
251            return {'users': [{'id': 123}]}
252        return {'users': []}
253
254    def _core_user_update_users(self, arg):
255        # fake Moodle core_user_update_users method.
256        if arg[0]['id'] == 'SCHOOL1-fault3':
257            raise xmlrpclib.Fault('faultCode', 'core_user_update_users failed')
258        return None
259
260
261class KofaAuthenticatorsIntegrationTests(unittest.TestCase):
262    # A test case where a fake Kofa server and a fake Moodle server
263    # are started before tests (and shut down afterwards).
264
265    kofa_server = None
266    kofa_th = None
267    moodle_server = None
268    moodle_th = None
269
270    @classmethod
271    def setUpClass(cls):
272        # set up a fake kofa server
273        cls.kofa_server = FakeKofaServer(('localhost', 6666))
274        cls.kofa_th = threading.Thread(target=cls.kofa_server.serve_forever)
275        cls.kofa_th.daemon = True
276        cls.kofa_th.start()
277        # set up a fake moodle server
278        cls.moodle_server = FakeMoodleServer(('localhost', 7777))
279        cls.moodle_th = threading.Thread(
280            target=cls.moodle_server.serve_forever)
281        cls.moodle_th.daemon = True
282        cls.moodle_th.start()
283
284    @classmethod
285    def tearDownClass(cls):
286        # tear down fake kofa server
287        cls.kofa_server.shutdown()
288        cls.kofa_server.server_close()
289        # tear down fake moodle server
290        cls.moodle_server.shutdown()
291        cls.moodle_server.server_close()
292
293    def test_fake_kofa_works(self):
294        # make sure the local fake kofa works
295        proxy = xmlrpclib.ServerProxy("http://localhost:6666", allow_none=True)
296        result = proxy.check_student_credentials('bird', 'bebop')
297        assert result == {
298            'description': 'Mr. Charles Parker',
299            'email': 'bird@gods.net',
300            'id': 'bird',
301            'type': 'student'}
302        result = proxy.check_applicant_credentials('pig', 'pog')
303        assert result == {
304            'description': 'Mr. Ray Charles',
305            'email': 'pig@gods.net',
306            'id': 'pig',
307            'type': 'applicant'}
308        result = proxy.get_student_moodle_data('pig')
309        assert result == {
310            'lastname': 'Charles',
311            'email': 'bb@bb.bb',
312            'firstname': 'Ray',
313            }
314        result = proxy.get_applicant_moodle_data('pig')
315        assert result == {
316            'lastname': 'Charles',
317            'email': 'bb@bb.bb',
318            'firstname': 'Ray',
319            }
320        return
321
322    def test_fake_moodle_works(self):
323        # make sure the local fake moodle works
324        proxy = xmlrpclib.ServerProxy("http://localhost:7777", allow_none=True)
325        # test core_user_create_users
326        result = proxy.core_user_create_users(
327            [{'username': 'any name'}])
328        assert result == [{'id': 37, 'username': 'any name'}]
329        self.assertRaises(
330            xmlrpclib.Fault, proxy.core_user_create_users,
331            [{'username': 'school1-fault1'}])
332        self.assertRaises(
333            xmlrpclib.Fault, proxy.core_user_create_users,
334            [{'username': 'school1-fault7'}])
335        # test core_user_get_users
336        self.assertRaises(
337            xmlrpclib.Fault, proxy.core_user_get_users,
338            [{'key': 'username', 'value': 'SCHOOL1-fault2'}])
339        # test core_user_update_users
340        result = proxy.core_user_update_users(
341            [{'id': 234}])
342        assert result == None
343        self.assertRaises(
344            xmlrpclib.Fault, proxy.core_user_update_users,
345            [{'id': 'SCHOOL1-fault3'}])
346        return
347
348    def test_check_credentials_KofaAuthenticator(self):
349        # we get real responses when querying Kofa instances
350        auth = KofaAuthenticator(auth_backends=str(BACKENDS2))
351        result1 = auth.check_credentials('SCHOOL1-bird', 'bebop')
352        assert result1 == (True, '')
353        result2 = auth.check_credentials('SCHOOL1-foo', 'bar')
354        assert result2 == (False, 'Invalid username or password.')
355        result3 = auth.check_credentials('SCHOOL2-bar', 'baz')
356        assert result3 == (False, 'Invalid username or password.')
357
358    def test_check_credentials_KofaMoodleAuthenticator(self):
359        # we get real responses when querying Kofa instances
360        auth = KofaMoodleAuthenticator(auth_backends=str(BACKENDS3))
361        result1s = auth.check_credentials('SCHOOL1-bird', 'bebop')
362        assert result1s == (True, '')
363        result1a = auth.check_credentials('SCHOOL1-pig', 'pog')
364        assert result1a == (True, '')
365        # user does not exist
366        result2s = auth.check_credentials('SCHOOL1-foo', 'bar')
367        assert result2s == (False, 'Invalid username or password')
368        # school does not exist
369        result3s = auth.check_credentials('SCHOOL2-bar', 'baz')
370        assert result3s == (False, 'Invalid username or password')
371        # user is neither a student nor an applicant
372        result8 = auth.check_credentials('SCHOOL1-fault6', 'biz')
373        assert result8 == (False, 'User not eligible')
374        # user has an existing email address
375        result9 = auth.check_credentials('SCHOOL1-fault7', 'biz')
376        assert result9 == (False,
377                    'Another Moodle user is using the same email address.'
378                    ' Email addresses can\'t be used twice in Moodle.')
379
380        # exception is catched if core_user_create_users fails
381        result4s = auth.check_credentials('SCHOOL1-fault1', 'biz')
382        assert result4s == (False, 'core_user_create_users failed')
383        # exception is catched if core_user_get_users fails
384        result5s = auth.check_credentials('SCHOOL1-fault2', 'biz')
385        assert result5s == (False, 'core_user_get_users failed')
386        # exception is catched if core_user_update_users fails
387        result6s = auth.check_credentials('SCHOOL1-fault3', 'biz')
388        assert result6s == (False, 'core_user_update_users failed')
389
390        # no exception is raised if user exists
391        result7s = auth.check_credentials('SCHOOL1-fault4', 'biz')
392        assert result7s == (True, '')
393        result7a = auth.check_credentials('SCHOOL1-fault5', 'biz')
394        assert result7a == (True, '')
395
396        # if marker is an empty string, school markers are neither
397        # checked nor removed
398        BACKENDS3['inst1']['marker'] = ''
399        auth = KofaMoodleAuthenticator(auth_backends=str(BACKENDS3))
400        result10s = auth.check_credentials('bird', 'bebop')
401        assert result10s == (True, '')
402        result10a = auth.check_credentials('pig', 'pog')
403        assert result10a == (True, '')
404        result11s = auth.check_credentials('SCHOOL1-bird', 'bebop')
405        assert result11s == (False, 'Invalid username or password')
406        result11a = auth.check_credentials('SCHOOL1-pig', 'pog')
407        assert result11a == (False, 'Invalid username or password')
Note: See TracBrowser for help on using the repository browser.