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