Fix #1025 - Make API IDs IRIs
[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
b0c8328e 26from mediagoblin.db.models import User
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
651403f0 83 ## At this point there should be no users in the database ;)
7d503a89 84 assert User.query.count() == 0
651403f0
CAW
85
86 # Successful register
87 # -------------------
ae3bc7fa 88 template.clear_test_template_context()
1972a888
CAW
89 response = test_app.post(
90 '/auth/register/', {
e1561d04 91 'username': u'angrygirl',
92 'password': 'iamsoangry',
93 'email': 'angrygrrl@example.org'})
1972a888
CAW
94 response.follow()
95
651403f0 96 ## Did we redirect to the proper page? Use the right template?
e1561d04 97 assert urlparse.urlsplit(response.location)[2] == '/u/angrygirl/'
515e3bd9 98 assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
1972a888 99
651403f0 100 ## Make sure user is in place
44082b12 101 new_user = mg_globals.database.User.query.filter_by(
e1561d04 102 username=u'angrygirl').first()
1972a888 103 assert new_user
1972a888 104
8e91df87 105 ## Make sure that the proper privileges are granted on registration
106
107 assert new_user.has_privilege(u'commenter')
108 assert new_user.has_privilege(u'uploader')
109 assert new_user.has_privilege(u'reporter')
110 assert not new_user.has_privilege(u'active')
f73f4c4b 111 ## Make sure user is logged in
ae3bc7fa 112 request = template.TEMPLATE_TEST_CONTEXT[
515e3bd9 113 'mediagoblin/user_pages/user_nonactive.html']['request']
e49b7e02 114 assert request.session['user_id'] == six.text_type(new_user.id)
f73f4c4b 115
1972a888 116 ## Make sure we get email confirmation, and try verifying
152a3bfa
AW
117 assert len(mail.EMAIL_TEST_INBOX) == 1
118 message = mail.EMAIL_TEST_INBOX.pop()
e1561d04 119 assert message['To'] == 'angrygrrl@example.org'
ae3bc7fa 120 email_context = template.TEMPLATE_TEST_CONTEXT[
1972a888 121 'mediagoblin/auth/verification_email.txt']
cda3055b 122 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
1972a888
CAW
123
124 path = urlparse.urlsplit(email_context['verification_url'])[2]
125 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
126 assert path == u'/auth/verify_email/'
127 parsed_get_params = urlparse.parse_qs(get_params)
128
7b1e17ed 129 ## Try verifying with bs verification key, shouldn't work
ae3bc7fa 130 template.clear_test_template_context()
a656ccd5 131 response = test_app.get(
342f06f7 132 "/auth/verify_email/?token=total_bs")
a656ccd5 133 response.follow()
342f06f7
RE
134
135 # Correct redirect?
136 assert urlparse.urlsplit(response.location)[2] == '/'
137
a656ccd5
CAW
138 # assert context['verification_successful'] == True
139 # TODO: Would be good to test messages here when we can do so...
44082b12 140 new_user = mg_globals.database.User.query.filter_by(
e1561d04 141 username=u'angrygirl').first()
7b1e17ed 142 assert new_user
7b1e17ed
CAW
143
144 ## Verify the email activation works
ae3bc7fa 145 template.clear_test_template_context()
a656ccd5
CAW
146 response = test_app.get("%s?%s" % (path, get_params))
147 response.follow()
ae3bc7fa 148 context = template.TEMPLATE_TEST_CONTEXT[
e054ae9b 149 'mediagoblin/user_pages/user.html']
a656ccd5
CAW
150 # assert context['verification_successful'] == True
151 # TODO: Would be good to test messages here when we can do so...
44082b12 152 new_user = mg_globals.database.User.query.filter_by(
e1561d04 153 username=u'angrygirl').first()
7b1e17ed 154 assert new_user
1972a888 155
cb9bac0c
CAW
156 # Uniqueness checks
157 # -----------------
158 ## We shouldn't be able to register with that user twice
ae3bc7fa 159 template.clear_test_template_context()
8a869db8
CAW
160 response = test_app.post(
161 '/auth/register/', {
e1561d04 162 'username': u'angrygirl',
163 'password': 'iamsoangry2',
164 'email': 'angrygrrl2@example.org'})
757690cc 165
ae3bc7fa 166 context = template.TEMPLATE_TEST_CONTEXT[
8a869db8
CAW
167 'mediagoblin/auth/register.html']
168 form = context['register_form']
169 assert form.username.errors == [
170 u'Sorry, a user with that name already exists.']
651403f0 171
8a869db8 172 ## TODO: Also check for double instances of an email address?
757690cc 173
f339b76a
RE
174 ### Oops, forgot the password
175 # -------------------
176 template.clear_test_template_context()
177 response = test_app.post(
178 '/auth/forgot_password/',
e1561d04 179 {'username': u'angrygirl'})
f339b76a
RE
180 response.follow()
181
182 ## Did we redirect to the proper page? Use the right template?
183 assert urlparse.urlsplit(response.location)[2] == '/auth/login/'
184 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
185
186 ## Make sure link to change password is sent by email
187 assert len(mail.EMAIL_TEST_INBOX) == 1
188 message = mail.EMAIL_TEST_INBOX.pop()
e1561d04 189 assert message['To'] == 'angrygrrl@example.org'
f339b76a 190 email_context = template.TEMPLATE_TEST_CONTEXT[
af665c4e 191 'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
f339b76a 192 #TODO - change the name of verification_url to something forgot-password-ish
cda3055b 193 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
f339b76a
RE
194
195 path = urlparse.urlsplit(email_context['verification_url'])[2]
196 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
f339b76a 197 parsed_get_params = urlparse.parse_qs(get_params)
342f06f7 198 assert path == u'/auth/forgot_password/verify/'
f339b76a
RE
199
200 ## Try using a bs password-changing verification key, shouldn't work
201 template.clear_test_template_context()
202 response = test_app.get(
342f06f7
RE
203 "/auth/forgot_password/verify/?token=total_bs")
204 response.follow()
f339b76a 205
342f06f7
RE
206 # Correct redirect?
207 assert urlparse.urlsplit(response.location)[2] == '/'
f339b76a
RE
208
209 ## Verify step 1 of password-change works -- can see form to change password
210 template.clear_test_template_context()
211 response = test_app.get("%s?%s" % (path, get_params))
af665c4e
RE
212 assert 'mediagoblin/plugins/basic_auth/change_fp.html' in \
213 template.TEMPLATE_TEST_CONTEXT
f339b76a
RE
214
215 ## Verify step 2.1 of password-change works -- report success to user
216 template.clear_test_template_context()
217 response = test_app.post(
218 '/auth/forgot_password/verify/', {
e1561d04 219 'password': 'iamveryveryangry',
f339b76a
RE
220 'token': parsed_get_params['token']})
221 response.follow()
222 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
223
224 ## Verify step 2.2 of password-change works -- login w/ new password success
225 template.clear_test_template_context()
226 response = test_app.post(
227 '/auth/login/', {
e1561d04 228 'username': u'angrygirl',
229 'password': 'iamveryveryangry'})
f339b76a
RE
230
231 # User should be redirected
232 response.follow()
233 assert urlparse.urlsplit(response.location)[2] == '/'
234 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
235
5c2ece74 236def test_authentication_views(test_app):
757690cc
CM
237 """
238 Test logging in and logging out
239 """
240 # Make a new user
e1561d04 241 test_user = fixture_add_user()
757690cc 242
5adb906a 243
757690cc 244 # Get login
0a4cecdc 245 # ---------
757690cc 246 test_app.get('/auth/login/')
04453ccf 247 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
757690cc 248
0a4cecdc
CM
249 # Failed login - blank form
250 # -------------------------
ae3bc7fa 251 template.clear_test_template_context()
0a4cecdc 252 response = test_app.post('/auth/login/')
ae3bc7fa 253 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
254 form = context['login_form']
255 assert form.username.errors == [u'This field is required.']
0a4cecdc
CM
256
257 # Failed login - blank user
258 # -------------------------
ae3bc7fa 259 template.clear_test_template_context()
0a4cecdc
CM
260 response = test_app.post(
261 '/auth/login/', {
262 'password': u'toast'})
ae3bc7fa 263 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
264 form = context['login_form']
265 assert form.username.errors == [u'This field is required.']
266
267 # Failed login - blank password
268 # -----------------------------
ae3bc7fa 269 template.clear_test_template_context()
0a4cecdc
CM
270 response = test_app.post(
271 '/auth/login/', {
272 'username': u'chris'})
e4deacd9 273 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
0a4cecdc
CM
274
275 # Failed login - bad user
276 # -----------------------
ae3bc7fa 277 template.clear_test_template_context()
0a4cecdc
CM
278 response = test_app.post(
279 '/auth/login/', {
280 'username': u'steve',
281 'password': 'toast'})
ae3bc7fa 282 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
283 assert context['login_failed']
284
285 # Failed login - bad password
286 # ---------------------------
ae3bc7fa 287 template.clear_test_template_context()
0a4cecdc
CM
288 response = test_app.post(
289 '/auth/login/', {
290 'username': u'chris',
a89df961 291 'password': 'jam_and_ham'})
ae3bc7fa 292 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
0a4cecdc
CM
293 assert context['login_failed']
294
295 # Successful login
296 # ----------------
ae3bc7fa 297 template.clear_test_template_context()
757690cc
CM
298 response = test_app.post(
299 '/auth/login/', {
300 'username': u'chris',
301 'password': 'toast'})
0a4cecdc
CM
302
303 # User should be redirected
757690cc 304 response.follow()
7d503a89 305 assert urlparse.urlsplit(response.location)[2] == '/'
04453ccf 306 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
757690cc 307
0a4cecdc 308 # Make sure user is in the session
ae3bc7fa 309 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
0a4cecdc 310 session = context['request'].session
e49b7e02 311 assert session['user_id'] == six.text_type(test_user.id)
757690cc 312
0a4cecdc
CM
313 # Successful logout
314 # -----------------
ae3bc7fa 315 template.clear_test_template_context()
0a4cecdc
CM
316 response = test_app.get('/auth/logout/')
317
318 # Should be redirected to index page
319 response.follow()
7d503a89 320 assert urlparse.urlsplit(response.location)[2] == '/'
04453ccf 321 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
0a4cecdc
CM
322
323 # Make sure the user is not in the session
ae3bc7fa 324 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
0a4cecdc 325 session = context['request'].session
04453ccf 326 assert 'user_id' not in session
757690cc 327
12c231c8
CM
328 # User is redirected to custom URL if POST['next'] is set
329 # -------------------------------------------------------
ae3bc7fa 330 template.clear_test_template_context()
12c231c8
CM
331 response = test_app.post(
332 '/auth/login/', {
333 'username': u'chris',
334 'password': 'toast',
335 'next' : '/u/chris/'})
7d503a89 336 assert urlparse.urlsplit(response.location)[2] == '/u/chris/'
dfd3f561 337
8cf9d643 338 ## Verify that username is lowercased on login attempt
339 template.clear_test_template_context()
340 response = test_app.post(
341 '/auth/login/', {
342 'username': u'ANDREW',
343 'password': 'fuselage'})
344 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
345 form = context['login_form']
346
347 # Username should no longer be uppercased; it should be lowercased
348 assert not form.username.data == u'ANDREW'
349 assert form.username.data == u'andrew'
350
dfd3f561 351@pytest.fixture()
5101c469 352def authentication_disabled_app(request):
dfd3f561
RE
353 return get_app(
354 request,
355 mgoblin_config=pkg_resources.resource_filename(
356 'mediagoblin.tests.auth_configs',
5101c469 357 'authentication_disabled_appconfig.ini'))
dfd3f561
RE
358
359
5101c469 360def test_authentication_disabled_app(authentication_disabled_app):
dfd3f561 361 # app.auth should = false
2c901db0 362 assert mg_globals
dfd3f561
RE
363 assert mg_globals.app.auth is False
364
365 # Try to visit register page
366 template.clear_test_template_context()
5101c469 367 response = authentication_disabled_app.get('/auth/register/')
dfd3f561
RE
368 response.follow()
369
370 # Correct redirect?
371 assert urlparse.urlsplit(response.location)[2] == '/'
372 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
373
374 # Try to vist login page
375 template.clear_test_template_context()
5101c469 376 response = authentication_disabled_app.get('/auth/login/')
dfd3f561
RE
377 response.follow()
378
379 # Correct redirect?
380 assert urlparse.urlsplit(response.location)[2] == '/'
381 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
382
3b8c733b
RE
383 ## Test check_login_simple should return None
384 assert auth_tools.check_login_simple('test', 'simple') is None
f339b76a
RE
385
386 # Try to visit the forgot password page
387 template.clear_test_template_context()
5101c469 388 response = authentication_disabled_app.get('/auth/register/')
f339b76a
RE
389 response.follow()
390
391 # Correct redirect?
392 assert urlparse.urlsplit(response.location)[2] == '/'
393 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT