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