1
# This file is part of the Juju GUI, which lets users view and manage Juju
2
# environments within a graphical interface (https://launchpad.net/juju-gui).
3
# Copyright (C) 2013 Canonical Ltd.
5
# This program is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU Affero General Public License version 3, as published by
7
# the Free Software Foundation.
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# Affero General Public License for more details.
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
"""Tests for the Juju GUI server authentication management."""
21
from tornado.testing import LogTrapTestCase
23
from guiserver import auth
24
from guiserver.tests import helpers
27
class TestUser(unittest.TestCase):
29
def test_authenticated_repr(self):
30
# An authenticated user is correctly represented.
32
username='the-doctor', password='bad-wolf', is_authenticated=True)
33
expected = '<User: the-doctor (authenticated)>'
34
self.assertEqual(expected, repr(user))
36
def test_not_authenticated_repr(self):
37
# A not authenticated user is correctly represented.
39
username='the-doctor', password='bad-wolf', is_authenticated=False)
40
expected = '<User: the-doctor (not authenticated)>'
41
self.assertEqual(expected, repr(user))
43
def test_anonymous_repr(self):
44
# An anonymous user is correctly represented.
46
expected = '<User: anonymous (not authenticated)>'
47
self.assertEqual(expected, repr(user))
50
# The string representation of an user is correctly generated.
51
user = auth.User(username='the-doctor')
52
self.assertEqual('the-doctor', str(user))
55
class AuthMiddlewareTestMixin(object):
56
"""Include tests for the AuthMiddleware.
58
Subclasses must subclass one of the API test mixins in helpers.
62
self.user = auth.User()
63
self.auth = auth.AuthMiddleware(self.user, self.get_auth_backend())
65
def assert_user(self, username, password, is_authenticated):
66
"""Ensure the current user reflects the given values."""
68
self.assertEqual(username, user.username)
69
self.assertEqual(password, user.password)
70
self.assertEqual(is_authenticated, user.is_authenticated)
72
def test_login_request(self):
73
# The authentication process starts if a login request is processed.
74
request = self.make_login_request(username='user', password='passwd')
75
self.auth.process_request(request)
76
self.assertTrue(self.auth.in_progress())
77
self.assert_user('user', 'passwd', False)
79
def test_login_success(self):
80
# The user is logged in if the authentication process completes.
81
request = self.make_login_request(username='user', password='passwd')
82
self.auth.process_request(request)
83
response = self.make_login_response()
84
self.auth.process_response(response)
85
self.assertFalse(self.auth.in_progress())
86
self.assert_user('user', 'passwd', True)
88
def test_login_failure(self):
89
# The user is not logged in if the authentication process fails.
90
request = self.make_login_request()
91
self.auth.process_request(request)
92
response = self.make_login_response(successful=False)
93
self.auth.process_response(response)
94
self.assertFalse(self.auth.in_progress())
95
self.assert_user('', '', False)
97
def test_request_mismatch(self):
98
# The authentication fails if the request and response identifiers
100
request = self.make_login_request(
101
request_id=42, username='user', password='passwd')
102
self.auth.process_request(request)
103
response = self.make_login_response(request_id=47)
104
self.auth.process_response(response)
105
self.assertTrue(self.auth.in_progress())
106
self.assert_user('user', 'passwd', False)
108
def test_multiple_auth_requests(self):
109
# Only the last authentication request is taken into consideration.
110
request1 = self.make_login_request(request_id=1)
111
request2 = self.make_login_request(
112
request_id=2, username='user2', password='passwd2')
113
self.auth.process_request(request1)
114
self.auth.process_request(request2)
115
# The first response arrives.
116
response = self.make_login_response(request_id=1)
117
self.auth.process_response(response)
118
# The user is still not autheticated and the auth is in progress.
119
self.assertFalse(self.user.is_authenticated)
120
self.assertTrue(self.auth.in_progress())
121
# The second response arrives.
122
response = self.make_login_response(request_id=2)
123
self.auth.process_response(response)
124
# The user logged in and the auth process completed.
125
self.assert_user('user2', 'passwd2', True)
126
self.assertFalse(self.auth.in_progress())
128
def test_request_id_is_zero(self):
129
# The authentication process starts if a login request is processed
130
# and the request id is zero.
131
request = self.make_login_request(request_id=0)
132
self.auth.process_request(request)
133
self.assertTrue(self.auth.in_progress())
136
class TestGoAuthMiddleware(
137
helpers.GoAPITestMixin, AuthMiddlewareTestMixin,
138
LogTrapTestCase, unittest.TestCase):
142
class TestPythonAuthMiddleware(
143
helpers.PythonAPITestMixin, AuthMiddlewareTestMixin,
144
LogTrapTestCase, unittest.TestCase):
148
class BackendTestMixin(object):
149
"""Include tests for the authentication backends.
151
Subclasses must subclass one of the API test mixins in helpers.
155
self.backend = self.get_auth_backend()
157
def test_get_request_id(self):
158
# The request id is correctly returned.
159
request = self.make_login_request(request_id=42)
160
self.assertEqual(42, self.backend.get_request_id(request))
162
def test_get_request_id_failure(self):
163
# If the request id cannot be found, None is returned.
164
self.assertIsNone(self.backend.get_request_id({}))
166
def test_request_is_login(self):
167
# True is returned if a login request is passed.
168
request = self.make_login_request()
169
self.assertTrue(self.backend.request_is_login(request))
171
def test_get_credentials(self):
172
# The user name and password are returned parsing the login request.
173
request = self.make_login_request(username='user', password='passwd')
174
username, password = self.backend.get_credentials(request)
175
self.assertEqual('user', username)
176
self.assertEqual('passwd', password)
178
def test_login_succeeded(self):
179
# True is returned if the login attempt succeeded.
180
response = self.make_login_response()
181
self.assertTrue(self.backend.login_succeeded(response))
183
def test_login_failed(self):
184
# False is returned if the login attempt failed.
185
response = self.make_login_response(successful=False)
186
self.assertFalse(self.backend.login_succeeded(response))
190
helpers.GoAPITestMixin, BackendTestMixin, unittest.TestCase):
192
def test_request_is_not_login(self):
193
# False is returned if the passed data is not a login request.
200
'Params': {'AuthTag': 'user', 'Password': 'passwd'},
205
'Request': 'INVALID',
206
'Params': {'AuthTag': 'user', 'Password': 'passwd'},
212
'Params': {'Password': 'passwd'},
215
for request in requests:
216
is_login = self.backend.request_is_login(request)
217
self.assertFalse(is_login, request)
220
class TestPythonBackend(
221
helpers.PythonAPITestMixin, BackendTestMixin, unittest.TestCase):
223
def test_request_is_not_login(self):
224
# False is returned if the passed data is not a login request.
231
'password': 'passwd',
236
'password': 'passwd',
244
for request in requests:
245
is_login = self.backend.request_is_login(request)
246
self.assertFalse(is_login, request)