Change complimentary_task to complementary_tas
[mediagoblin.git] / mediagoblin / tests / test_auth.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4b5f4e87
CAW
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/>.
fd19da34 16
dfd3f561
RE
17import pkg_resources
18import pytest
4b5f4e87 19
e49b7e02
BP
20import six
21
fd19da34
BP
22import six.moves.urllib.parse as urlparse
23
7e55bcb8 24from mediagoblin import mg_globals
d88fcb03 25from mediagoblin.db.models import User, LocalUser
dfd3f561 26from mediagoblin.tests.tools import get_app, fixture_add_user
152a3bfa 27from mediagoblin.tools import template, mail
3b8c733b 28from mediagoblin.auth import tools as auth_tools
460ce564 29
4b5f4e87 30
1be247b3 31def test_register_views(test_app):
2fecc29d
CAW
32 """
33 Massive test function that all our registration-related views all work.
34 """
460ce564 35 # Test doing a simple GET on the page
651403f0
CAW
36 # -----------------------------------
37
460ce564
CAW
38 test_app.get('/auth/register/')
39 # Make sure it rendered with the appropriate template
04453ccf 40 assert 'mediagoblin/auth/register.html' in template.TEMPLATE_TEST_CONTEXT
757690cc 41
460ce564 42 # Try to register without providing anything, should error
651403f0
CAW
43 # --------------------------------------------------------
44
ae3bc7fa 45 template.clear_test_template_context()
460ce564
CAW
46 test_app.post(
47 '/auth/register/', {})
ae3bc7fa 48 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
460ce564
CAW
49 form = context['register_form']
50 assert form.username.errors == [u'This field is required.']
51 assert form.password.errors == [u'This field is required.']
460ce564 52 assert form.email.errors == [u'This field is required.']
651403f0
CAW
53
54 # Try to register with fields that are known to be invalid
55 # --------------------------------------------------------
56
57 ## too short
ae3bc7fa 58 template.clear_test_template_context()
651403f0
CAW
59 test_app.post(
60 '/auth/register/', {
61 'username': 'l',
62 'password': 'o',
651403f0 63 'email': 'l'})
ae3bc7fa 64 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
651403f0
CAW
65 form = context['register_form']
66
7d503a89
CAW
67 assert form.username.errors == [u'Field must be between 3 and 30 characters long.']
68 assert form.password.errors == [u'Field must be between 5 and 1024 characters long.']
651403f0
CAW
69
70 ## bad form
ae3bc7fa 71 template.clear_test_template_context()
651403f0
CAW
72 test_app.post(
73 '/auth/register/', {
74 'username': '@_@',
75 'email': 'lollerskates'})
ae3bc7fa 76 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
651403f0
CAW
77 form = context['register_form']
78
7d503a89
CAW
79 assert form.username.errors == [u'This field does not take email addresses.']
80 assert form.email.errors == [u'This field requires an email address.']
651403f0 81
bbe08556
LD
82 ## invalid characters
83 template.clear_test_template_context()
84 test_app.post(
85 '/auth/register/', {
86 'username': 'ampersand&invalid',
87 'email': 'easter@egg.com'})
88 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
89 form = context['register_form']
90
91 assert form.username.errors == [u'Invalid input.']
92
651403f0 93 ## At this point there should be no users in the database ;)
7d503a89 94 assert User.query.count() == 0
651403f0 95
bbe08556
LD
96 ## mixture of characters from all valid ranges
97 template.clear_test_template_context()
98 test_app.post(
99 '/auth/register/', {
100 'username': 'Jean-Louis1_Le-Chat',
101 'password': 'iamsohappy',
102 'email': 'easter@egg.com'})
103
104 ## At this point there should on user in the database
105 assert User.query.count() == 1
106
651403f0
CAW
107 # Successful register
108 # -------------------
ae3bc7fa 109 template.clear_test_template_context()
1972a888
CAW
110 response = test_app.post(
111 '/auth/register/', {
e1561d04 112 'username': u'angrygirl',
113 'password': 'iamsoangry',
114 'email': 'angrygrrl@example.org'})
1972a888
CAW
115 response.follow()
116
651403f0 117 ## Did we redirect to the proper page? Use the right template?
e1561d04 118 assert urlparse.urlsplit(response.location)[2] == '/u/angrygirl/'
515e3bd9 119 assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
1972a888 120
651403f0 121 ## Make sure user is in place
b4997540
JT
122 new_user = mg_globals.database.LocalUser.query.filter(
123 LocalUser.username==u'angrygirl'
d88fcb03 124 ).first()
1972a888 125 assert new_user
1972a888 126
8e91df87 127 ## Make sure that the proper privileges are granted on registration
128
129 assert new_user.has_privilege(u'commenter')
130 assert new_user.has_privilege(u'uploader')
131 assert new_user.has_privilege(u'reporter')
132 assert not new_user.has_privilege(u'active')
f73f4c4b 133 ## Make sure user is logged in
ae3bc7fa 134 request = template.TEMPLATE_TEST_CONTEXT[
515e3bd9 135 'mediagoblin/user_pages/user_nonactive.html']['request']
e49b7e02 136 assert request.session['user_id'] == six.text_type(new_user.id)
f73f4c4b 137
1972a888 138 ## Make sure we get email confirmation, and try verifying
bbe08556 139 assert len(mail.EMAIL_TEST_INBOX) == 2
152a3bfa 140 message = mail.EMAIL_TEST_INBOX.pop()
e1561d04 141 assert message['To'] == 'angrygrrl@example.org'
ae3bc7fa 142 email_context = template.TEMPLATE_TEST_CONTEXT[
1972a888 143 'mediagoblin/auth/verification_email.txt']
cda3055b 144 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
1972a888
CAW
145
146 path = urlparse.urlsplit(email_context['verification_url'])[2]
147 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
148 assert path == u'/auth/verify_email/'
149 parsed_get_params = urlparse.parse_qs(get_params)
150
7b1e17ed 151 ## Try verifying with bs verification key, shouldn't work
ae3bc7fa 152 template.clear_test_template_context()
a656ccd5 153 response = test_app.get(
342f06f7 154 "/auth/verify_email/?token=total_bs")
a656ccd5 155 response.follow()
342f06f7
RE
156
157 # Correct redirect?
158 assert urlparse.urlsplit(response.location)[2] == '/'
159
a656ccd5
CAW
160 # assert context['verification_successful'] == True
161 # TODO: Would be good to test messages here when we can do so...
b4997540 162 new_user = mg_globals.database.LocalUser.query.filter(
d88fcb03
JT
163 LocalUser.username==u'angrygirl'
164 ).first()
7b1e17ed 165 assert new_user
7b1e17ed
CAW
166
167 ## Verify the email activation works
ae3bc7fa 168 template.clear_test_template_context()
a656ccd5
CAW
169 response = test_app.get("%s?%s" % (path, get_params))
170 response.follow()
ae3bc7fa 171 context = template.TEMPLATE_TEST_CONTEXT[
e054ae9b 172 'mediagoblin/user_pages/user.html']
a656ccd5
CAW
173 # assert context['verification_successful'] == True
174 # TODO: Would be good to test messages here when we can do so...
b4997540 175 new_user = mg_globals.database.LocalUser.query.filter(
d88fcb03
JT
176 LocalUser.username==u'angrygirl'
177 ).first()
7b1e17ed 178 assert new_user
1972a888 179
cb9bac0c
CAW
180 # Uniqueness checks
181 # -----------------
182 ## We shouldn't be able to register with that user twice
ae3bc7fa 183 template.clear_test_template_context()
8a869db8
CAW
184 response = test_app.post(
185 '/auth/register/', {
e1561d04 186 'username': u'angrygirl',
187 'password': 'iamsoangry2',
188 'email': 'angrygrrl2@example.org'})
757690cc 189
ae3bc7fa 190 context = template.TEMPLATE_TEST_CONTEXT[
8a869db8
CAW
191 'mediagoblin/auth/register.html']
192 form = context['register_form']
193 assert form.username.errors == [
194 u'Sorry, a user with that name already exists.']
651403f0 195
8a869db8 196 ## TODO: Also check for double instances of an email address?
757690cc 197
f339b76a
RE
198 ### Oops, forgot the password
199 # -------------------
200 template.clear_test_template_context()
201 response = test_app.post(
202 '/auth/forgot_password/',
e1561d04 203 {'username': u'angrygirl'})
f339b76a
RE
204 response.follow()
205
206 ## Did we redirect to the proper page? Use the right template?
207 assert urlparse.urlsplit(response.location)[2] == '/auth/login/'
208 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
209
210 ## Make sure link to change password is sent by email
bbe08556 211 assert len(mail.EMAIL_TEST_INBOX) == 2
f339b76a 212 message = mail.EMAIL_TEST_INBOX.pop()
e1561d04 213 assert message['To'] == 'angrygrrl@example.org'
f339b76a 214 email_context = template.TEMPLATE_TEST_CONTEXT[
af665c4e 215 'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
f339b76a 216 #TODO - change the name of verification_url to something forgot-password-ish
cda3055b 217 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
f339b76a
RE
218
219 path = urlparse.urlsplit(email_context['verification_url'])[2]
220 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
f339b76a 221 parsed_get_params = urlparse.parse_qs(get_params)
342f06f7 222 assert path == u'/auth/forgot_password/verify/'
f339b76a
RE
223
224 ## Try using a bs password-changing verification key, shouldn't work
225 template.clear_test_template_context()
226 response = test_app.get(
342f06f7
RE
227 "/auth/forgot_password/verify/?token=total_bs")
228 response.follow()
f339b76a 229
342f06f7
RE
230 # Correct redirect?
231 assert urlparse.urlsplit(response.location)[2] == '/'
f339b76a
RE
232
233 ## Verify step 1 of password-change works -- can see form to change password
234 template.clear_test_template_context()
235 response = test_app.get("%s?%s" % (path, get_params))
af665c4e
RE
236 assert 'mediagoblin/plugins/basic_auth/change_fp.html' in \
237 template.TEMPLATE_TEST_CONTEXT
f339b76a
RE
238
239 ## Verify step 2.1 of password-change works -- report success to user
240 template.clear_test_template_context()
241 response = test_app.post(
242 '/auth/forgot_password/verify/', {
e1561d04 243 'password': 'iamveryveryangry',
f339b76a
RE
244 'token': parsed_get_params['token']})
245 response.follow()
246 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
247
248 ## Verify step 2.2 of password-change works -- login w/ new password success
249 template.clear_test_template_context()
250 response = test_app.post(
251 '/auth/login/', {
e1561d04 252 'username': u'angrygirl',
253 'password': 'iamveryveryangry'})
f339b76a
RE
254
255 # User should be redirected
256 response.follow()
257 assert urlparse.urlsplit(response.location)[2] == '/'
258 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
259
5c2ece74 260def test_authentication_views(test_app):
757690cc
CM
261 """
262 Test logging in and logging out
263 """
264 # Make a new user
e1561d04 265 test_user = fixture_add_user()
757690cc 266
5adb906a 267
757690cc 268 # Get login
0a4cecdc 269 # ---------
757690cc 270 test_app.get('/auth/login/')
04453ccf 271 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
757690cc 272
0a4cecdc
CM
273 # Failed login - blank form
274 # -------------------------
ae3bc7fa 275 template.clear_test_template_context()
0a4cecdc 276 response = test_app.post('/auth/login/')
ae3bc7fa 277 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
278 form = context['login_form']
279 assert form.username.errors == [u'This field is required.']
0a4cecdc
CM
280
281 # Failed login - blank user
282 # -------------------------
ae3bc7fa 283 template.clear_test_template_context()
0a4cecdc
CM
284 response = test_app.post(
285 '/auth/login/', {
286 'password': u'toast'})
ae3bc7fa 287 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
288 form = context['login_form']
289 assert form.username.errors == [u'This field is required.']
290
291 # Failed login - blank password
292 # -----------------------------
ae3bc7fa 293 template.clear_test_template_context()
0a4cecdc
CM
294 response = test_app.post(
295 '/auth/login/', {
296 'username': u'chris'})
e4deacd9 297 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
0a4cecdc
CM
298
299 # Failed login - bad user
300 # -----------------------
ae3bc7fa 301 template.clear_test_template_context()
0a4cecdc
CM
302 response = test_app.post(
303 '/auth/login/', {
304 'username': u'steve',
305 'password': 'toast'})
ae3bc7fa 306 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
307 assert context['login_failed']
308
309 # Failed login - bad password
310 # ---------------------------
ae3bc7fa 311 template.clear_test_template_context()
0a4cecdc
CM
312 response = test_app.post(
313 '/auth/login/', {
314 'username': u'chris',
a89df961 315 'password': 'jam_and_ham'})
ae3bc7fa 316 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
317 assert context['login_failed']
318
319 # Successful login
320 # ----------------
ae3bc7fa 321 template.clear_test_template_context()
757690cc
CM
322 response = test_app.post(
323 '/auth/login/', {
324 'username': u'chris',
325 'password': 'toast'})
0a4cecdc
CM
326
327 # User should be redirected
757690cc 328 response.follow()
7d503a89 329 assert urlparse.urlsplit(response.location)[2] == '/'
04453ccf 330 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
757690cc 331
0a4cecdc 332 # Make sure user is in the session
ae3bc7fa 333 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
0a4cecdc 334 session = context['request'].session
e49b7e02 335 assert session['user_id'] == six.text_type(test_user.id)
757690cc 336
0a4cecdc
CM
337 # Successful logout
338 # -----------------
ae3bc7fa 339 template.clear_test_template_context()
0a4cecdc
CM
340 response = test_app.get('/auth/logout/')
341
342 # Should be redirected to index page
343 response.follow()
7d503a89 344 assert urlparse.urlsplit(response.location)[2] == '/'
04453ccf 345 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
0a4cecdc
CM
346
347 # Make sure the user is not in the session
ae3bc7fa 348 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
0a4cecdc 349 session = context['request'].session
04453ccf 350 assert 'user_id' not in session
757690cc 351
12c231c8
CM
352 # User is redirected to custom URL if POST['next'] is set
353 # -------------------------------------------------------
ae3bc7fa 354 template.clear_test_template_context()
12c231c8
CM
355 response = test_app.post(
356 '/auth/login/', {
357 'username': u'chris',
358 'password': 'toast',
359 'next' : '/u/chris/'})
7d503a89 360 assert urlparse.urlsplit(response.location)[2] == '/u/chris/'
dfd3f561 361
8cf9d643 362 ## Verify that username is lowercased on login attempt
363 template.clear_test_template_context()
364 response = test_app.post(
365 '/auth/login/', {
366 'username': u'ANDREW',
367 'password': 'fuselage'})
368 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
369 form = context['login_form']
370
371 # Username should no longer be uppercased; it should be lowercased
372 assert not form.username.data == u'ANDREW'
373 assert form.username.data == u'andrew'
374
d7562065 375 # Successful login with short user
bf2f121a 376 # --------------------------------
d7562065
JS
377 short_user = fixture_add_user(username=u'me', password=u'sho')
378 template.clear_test_template_context()
379 response = test_app.post(
380 '/auth/login/', {
381 'username': u'me',
382 'password': 'sho'})
383
d7562065 384 # User should be redirected
d7562065 385 response.follow()
bf2f121a 386
d7562065
JS
387 assert urlparse.urlsplit(response.location)[2] == '/'
388 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
389
390 # Make sure user is in the session
391 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
392 session = context['request'].session
393 assert session['user_id'] == six.text_type(short_user.id)
394
bf2f121a
JS
395 # Must logout
396 template.clear_test_template_context()
397 response = test_app.get('/auth/logout/')
398
d7562065
JS
399 # Successful login with long user
400 # ----------------
401 long_user = fixture_add_user(
402 username=u'realllylonguser@reallylongdomain.com.co', password=u'sho')
403 template.clear_test_template_context()
404 response = test_app.post(
405 '/auth/login/', {
bf2f121a 406 'username': u'realllylonguser@reallylongdomain.com.co',
d7562065
JS
407 'password': 'sho'})
408
d7562065 409 # User should be redirected
d7562065
JS
410 response.follow()
411 assert urlparse.urlsplit(response.location)[2] == '/'
412 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
413
414 # Make sure user is in the session
415 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
416 session = context['request'].session
bf2f121a
JS
417 assert session['user_id'] == six.text_type(long_user.id)
418
419 template.clear_test_template_context()
420 response = test_app.get('/auth/logout/')
d7562065 421
dfd3f561 422@pytest.fixture()
5101c469 423def authentication_disabled_app(request):
dfd3f561
RE
424 return get_app(
425 request,
426 mgoblin_config=pkg_resources.resource_filename(
427 'mediagoblin.tests.auth_configs',
5101c469 428 'authentication_disabled_appconfig.ini'))
dfd3f561
RE
429
430
5101c469 431def test_authentication_disabled_app(authentication_disabled_app):
dfd3f561 432 # app.auth should = false
2c901db0 433 assert mg_globals
dfd3f561
RE
434 assert mg_globals.app.auth is False
435
436 # Try to visit register page
437 template.clear_test_template_context()
5101c469 438 response = authentication_disabled_app.get('/auth/register/')
dfd3f561
RE
439 response.follow()
440
441 # Correct redirect?
442 assert urlparse.urlsplit(response.location)[2] == '/'
443 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
444
445 # Try to vist login page
446 template.clear_test_template_context()
5101c469 447 response = authentication_disabled_app.get('/auth/login/')
dfd3f561
RE
448 response.follow()
449
450 # Correct redirect?
451 assert urlparse.urlsplit(response.location)[2] == '/'
452 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
453
3b8c733b
RE
454 ## Test check_login_simple should return None
455 assert auth_tools.check_login_simple('test', 'simple') is None
f339b76a
RE
456
457 # Try to visit the forgot password page
458 template.clear_test_template_context()
5101c469 459 response = authentication_disabled_app.get('/auth/register/')
f339b76a
RE
460 response.follow()
461
462 # Correct redirect?
463 assert urlparse.urlsplit(response.location)[2] == '/'
464 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT