At this point, I am very close to done with this code! I made one big change at
[mediagoblin.git] / mediagoblin / tests / test_openid.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 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 Affero General Public License for more details.
13 #
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/>.
16
17 import urlparse
18 import pkg_resources
19 import pytest
20 import mock
21
22 openid_consumer = pytest.importorskip(
23 "openid.consumer.consumer")
24
25 from mediagoblin import mg_globals
26 from mediagoblin.db.base import Session
27 from mediagoblin.db.models import User
28 from mediagoblin.plugins.openid.models import OpenIDUserURL
29 from mediagoblin.tests.tools import get_app, fixture_add_user
30 from mediagoblin.tools import template
31
32 # App with plugin enabled
33 @pytest.fixture()
34 def openid_plugin_app(request):
35 return get_app(
36 request,
37 mgoblin_config=pkg_resources.resource_filename(
38 'mediagoblin.tests.auth_configs',
39 'openid_appconfig.ini'))
40
41
42 class TestOpenIDPlugin(object):
43 def _setup(self, openid_plugin_app, value=True, edit=False, delete=False):
44 if value:
45 response = openid_consumer.SuccessResponse(mock.Mock(), mock.Mock())
46 if edit or delete:
47 response.identity_url = u'http://add.myopenid.com'
48 else:
49 response.identity_url = u'http://real.myopenid.com'
50 self._finish_verification = mock.Mock(return_value=response)
51 else:
52 self._finish_verification = mock.Mock(return_value=False)
53
54 @mock.patch('mediagoblin.plugins.openid.views._response_email', mock.Mock(return_value=None))
55 @mock.patch('mediagoblin.plugins.openid.views._response_nickname', mock.Mock(return_value=None))
56 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
57 def _setup_start(self, openid_plugin_app, edit, delete):
58 if edit:
59 self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
60 '/edit/openid/finish/'))
61 elif delete:
62 self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
63 '/edit/openid/delete/finish/'))
64 else:
65 self._start_verification = mock.Mock(return_value=openid_plugin_app.post(
66 '/auth/openid/login/finish/'))
67 _setup_start(self, openid_plugin_app, edit, delete)
68
69 def test_bad_login(self, openid_plugin_app):
70 """ Test that attempts to login with invalid paramaters"""
71
72 # Test GET request for auth/register page
73 res = openid_plugin_app.get('/auth/register/').follow()
74
75 # Make sure it redirected to the correct place
76 assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
77
78 # Test GET request for auth/login page
79 res = openid_plugin_app.get('/auth/login/')
80 res.follow()
81
82 # Correct redirect?
83 assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
84
85 # Test GET request for auth/openid/register page
86 res = openid_plugin_app.get('/auth/openid/register/')
87 res.follow()
88
89 # Correct redirect?
90 assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
91
92 # Test GET request for auth/openid/login/finish page
93 res = openid_plugin_app.get('/auth/openid/login/finish/')
94 res.follow()
95
96 # Correct redirect?
97 assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
98
99 # Test GET request for auth/openid/login page
100 res = openid_plugin_app.get('/auth/openid/login/')
101
102 # Correct place?
103 assert 'mediagoblin/plugins/openid/login.html' in template.TEMPLATE_TEST_CONTEXT
104
105 # Try to login with an empty form
106 template.clear_test_template_context()
107 openid_plugin_app.post(
108 '/auth/openid/login/', {})
109 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
110 form = context['login_form']
111 assert form.openid.errors == [u'This field is required.']
112
113 # Try to login with wrong form values
114 template.clear_test_template_context()
115 openid_plugin_app.post(
116 '/auth/openid/login/', {
117 'openid': 'not_a_url.com'})
118 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
119 form = context['login_form']
120 assert form.openid.errors == [u'Please enter a valid url.']
121
122 # Should be no users in the db
123 assert User.query.count() == 0
124
125 # Phony OpenID URl
126 template.clear_test_template_context()
127 openid_plugin_app.post(
128 '/auth/openid/login/', {
129 'openid': 'http://phoney.myopenid.com/'})
130 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/login.html']
131 form = context['login_form']
132 assert form.openid.errors == [u'Sorry, the OpenID server could not be found']
133
134 def test_login(self, openid_plugin_app):
135 """Tests that test login and registion with openid"""
136 # Test finish_login redirects correctly when response = False
137 self._setup(openid_plugin_app, False)
138
139 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
140 @mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
141 def _test_non_response():
142 template.clear_test_template_context()
143 res = openid_plugin_app.post(
144 '/auth/openid/login/', {
145 'openid': 'http://phoney.myopenid.com/'})
146 res.follow()
147
148 # Correct Place?
149 assert urlparse.urlsplit(res.location)[2] == '/auth/openid/login/'
150 assert 'mediagoblin/plugins/openid/login.html' in template.TEMPLATE_TEST_CONTEXT
151 _test_non_response()
152
153 # Test login with new openid
154 # Need to clear_test_template_context before calling _setup
155 template.clear_test_template_context()
156 self._setup(openid_plugin_app)
157
158 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
159 @mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
160 def _test_new_user():
161 openid_plugin_app.post(
162 '/auth/openid/login/', {
163 'openid': u'http://real.myopenid.com'})
164
165 # Right place?
166 assert 'mediagoblin/auth/register.html' in template.TEMPLATE_TEST_CONTEXT
167 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
168 register_form = context['register_form']
169
170 # Register User
171 res = openid_plugin_app.post(
172 '/auth/openid/register/', {
173 'openid': register_form.openid.data,
174 'username': u'chris',
175 'email': u'chris@example.com'})
176 res.follow()
177
178 # Correct place?
179 assert urlparse.urlsplit(res.location)[2] == '/u/chris/'
180 assert 'mediagoblin/user_pages/user.html' in template.TEMPLATE_TEST_CONTEXT
181
182 # No need to test if user is in logged in and verification email
183 # awaits, since openid uses the register_user function which is
184 # tested in test_auth
185
186 # Logout User
187 openid_plugin_app.get('/auth/logout')
188
189 # Get user and detach from session
190 test_user = mg_globals.database.User.query.filter_by(
191 username=u'chris').first()
192 Session.expunge(test_user)
193
194 # Log back in
195 # Could not get it to work by 'POST'ing to /auth/openid/login/
196 template.clear_test_template_context()
197 res = openid_plugin_app.post(
198 '/auth/openid/login/finish/', {
199 'openid': u'http://real.myopenid.com'})
200 res.follow()
201
202 assert urlparse.urlsplit(res.location)[2] == '/'
203 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
204
205 # Make sure user is in the session
206 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
207 session = context['request'].session
208 assert session['user_id'] == unicode(test_user.id)
209
210 _test_new_user()
211
212 # Test register with empty form
213 template.clear_test_template_context()
214 openid_plugin_app.post(
215 '/auth/openid/register/', {})
216 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
217 register_form = context['register_form']
218
219 assert register_form.openid.errors == [u'This field is required.']
220 assert register_form.email.errors == [u'This field is required.']
221 assert register_form.username.errors == [u'This field is required.']
222
223 # Try to register with existing username and email
224 template.clear_test_template_context()
225 openid_plugin_app.post(
226 '/auth/openid/register/', {
227 'openid': 'http://real.myopenid.com',
228 'email': 'chris@example.com',
229 'username': 'chris'})
230 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
231 register_form = context['register_form']
232
233 assert register_form.username.errors == [u'Sorry, a user with that name already exists.']
234 assert register_form.email.errors == [u'Sorry, a user with that email address already exists.']
235 assert register_form.openid.errors == [u'Sorry, an account is already registered to that OpenID.']
236
237 def test_add_delete(self, openid_plugin_app):
238 """Test adding and deleting openids"""
239 # Add user
240 test_user = fixture_add_user(password='', privileges=[u'active'])
241 openid = OpenIDUserURL()
242 openid.openid_url = 'http://real.myopenid.com'
243 openid.user_id = test_user.id
244 openid.save()
245
246 # Log user in
247 template.clear_test_template_context()
248 self._setup(openid_plugin_app)
249
250 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
251 @mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
252 def _login_user():
253 openid_plugin_app.post(
254 '/auth/openid/login/finish/', {
255 'openid': u'http://real.myopenid.com'})
256
257 _login_user()
258
259 # Try and delete only OpenID url
260 template.clear_test_template_context()
261 res = openid_plugin_app.post(
262 '/edit/openid/delete/', {
263 'openid': 'http://real.myopenid.com'})
264 assert 'mediagoblin/plugins/openid/delete.html' in template.TEMPLATE_TEST_CONTEXT
265
266 # Add OpenID to user
267 # Empty form
268 template.clear_test_template_context()
269 res = openid_plugin_app.post(
270 '/edit/openid/', {})
271 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
272 form = context['form']
273 assert form.openid.errors == [u'This field is required.']
274
275 # Try with a bad url
276 template.clear_test_template_context()
277 openid_plugin_app.post(
278 '/edit/openid/', {
279 'openid': u'not_a_url.com'})
280 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
281 form = context['form']
282 assert form.openid.errors == [u'Please enter a valid url.']
283
284 # Try with a url that's already registered
285 template.clear_test_template_context()
286 openid_plugin_app.post(
287 '/edit/openid/', {
288 'openid': 'http://real.myopenid.com'})
289 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/add.html']
290 form = context['form']
291 assert form.openid.errors == [u'Sorry, an account is already registered to that OpenID.']
292
293 # Test adding openid to account
294 # Need to clear_test_template_context before calling _setup
295 template.clear_test_template_context()
296 self._setup(openid_plugin_app, edit=True)
297
298 # Need to remove openid_url from db because it was added at setup
299 openid = OpenIDUserURL.query.filter_by(
300 openid_url=u'http://add.myopenid.com')
301 openid.delete()
302
303 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
304 @mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
305 def _test_add():
306 # Successful add
307 template.clear_test_template_context()
308 res = openid_plugin_app.post(
309 '/edit/openid/', {
310 'openid': u'http://add.myopenid.com'})
311 res.follow()
312
313 # Correct place?
314 assert urlparse.urlsplit(res.location)[2] == '/edit/account/'
315 assert 'mediagoblin/edit/edit_account.html' in template.TEMPLATE_TEST_CONTEXT
316
317 # OpenID Added?
318 new_openid = mg_globals.database.OpenIDUserURL.query.filter_by(
319 openid_url=u'http://add.myopenid.com').first()
320 assert new_openid
321
322 _test_add()
323
324 # Test deleting openid from account
325 # Need to clear_test_template_context before calling _setup
326 template.clear_test_template_context()
327 self._setup(openid_plugin_app, delete=True)
328
329 # Need to add OpenID back to user because it was deleted during
330 # patch
331 openid = OpenIDUserURL()
332 openid.openid_url = 'http://add.myopenid.com'
333 openid.user_id = test_user.id
334 openid.save()
335
336 @mock.patch('mediagoblin.plugins.openid.views._finish_verification', self._finish_verification)
337 @mock.patch('mediagoblin.plugins.openid.views._start_verification', self._start_verification)
338 def _test_delete(self, test_user):
339 # Delete openid from user
340 # Create another user to test deleting OpenID that doesn't belong to them
341 new_user = fixture_add_user(username='newman')
342 openid = OpenIDUserURL()
343 openid.openid_url = 'http://realfake.myopenid.com/'
344 openid.user_id = new_user.id
345 openid.save()
346
347 # Try and delete OpenID url that isn't the users
348 template.clear_test_template_context()
349 res = openid_plugin_app.post(
350 '/edit/openid/delete/', {
351 'openid': 'http://realfake.myopenid.com/'})
352 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/plugins/openid/delete.html']
353 form = context['form']
354 assert form.openid.errors == [u'That OpenID is not registered to this account.']
355
356 # Delete OpenID
357 # Kind of weird to POST to delete/finish
358 template.clear_test_template_context()
359 res = openid_plugin_app.post(
360 '/edit/openid/delete/finish/', {
361 'openid': u'http://add.myopenid.com'})
362 res.follow()
363
364 # Correct place?
365 assert urlparse.urlsplit(res.location)[2] == '/edit/account/'
366 assert 'mediagoblin/edit/edit_account.html' in template.TEMPLATE_TEST_CONTEXT
367
368 # OpenID deleted?
369 new_openid = mg_globals.database.OpenIDUserURL.query.filter_by(
370 openid_url=u'http://add.myopenid.com').first()
371 assert not new_openid
372
373 _test_delete(self, test_user)