Fix #5391 - Alembic migrations would only work for SQLite
[larjonas-mediagoblin.git] / mediagoblin / tests / test_auth.py
blob62f77f7432a2e533cdacded471709199c3027241
2 # GNU MediaGoblin -- federated, autonomous media hosting
3 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
18 import pkg_resources
19 import pytest
21 import six
23 import six.moves.urllib.parse as urlparse
25 from mediagoblin import mg_globals
26 from mediagoblin.db.models import User, LocalUser
27 from mediagoblin.tests.tools import get_app, fixture_add_user
28 from mediagoblin.tools import template, mail
29 from mediagoblin.auth import tools as auth_tools
32 def test_register_views(test_app):
33 """
34 Massive test function that all our registration-related views all work.
35 """
36 # Test doing a simple GET on the page
37 # -----------------------------------
39 test_app.get('/auth/register/')
40 # Make sure it rendered with the appropriate template
41 assert 'mediagoblin/auth/register.html' in template.TEMPLATE_TEST_CONTEXT
43 # Try to register without providing anything, should error
44 # --------------------------------------------------------
46 template.clear_test_template_context()
47 test_app.post(
48 '/auth/register/', {})
49 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
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.']
53 assert form.email.errors == [u'This field is required.']
55 # Try to register with fields that are known to be invalid
56 # --------------------------------------------------------
58 ## too short
59 template.clear_test_template_context()
60 test_app.post(
61 '/auth/register/', {
62 'username': 'l',
63 'password': 'o',
64 'email': 'l'})
65 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
66 form = context['register_form']
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.']
71 ## bad form
72 template.clear_test_template_context()
73 test_app.post(
74 '/auth/register/', {
75 'username': '@_@',
76 'email': 'lollerskates'})
77 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
78 form = context['register_form']
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.']
83 ## At this point there should be no users in the database ;)
84 assert User.query.count() == 0
86 # Successful register
87 # -------------------
88 template.clear_test_template_context()
89 response = test_app.post(
90 '/auth/register/', {
91 'username': u'angrygirl',
92 'password': 'iamsoangry',
93 'email': 'angrygrrl@example.org'})
94 response.follow()
96 ## Did we redirect to the proper page? Use the right template?
97 assert urlparse.urlsplit(response.location)[2] == '/u/angrygirl/'
98 assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
100 ## Make sure user is in place
101 new_user = mg_globals.database.LocalUser.query.filter(
102 LocalUser.username==u'angrygirl'
103 ).first()
104 assert new_user
106 ## Make sure that the proper privileges are granted on registration
108 assert new_user.has_privilege(u'commenter')
109 assert new_user.has_privilege(u'uploader')
110 assert new_user.has_privilege(u'reporter')
111 assert not new_user.has_privilege(u'active')
112 ## Make sure user is logged in
113 request = template.TEMPLATE_TEST_CONTEXT[
114 'mediagoblin/user_pages/user_nonactive.html']['request']
115 assert request.session['user_id'] == six.text_type(new_user.id)
117 ## Make sure we get email confirmation, and try verifying
118 assert len(mail.EMAIL_TEST_INBOX) == 1
119 message = mail.EMAIL_TEST_INBOX.pop()
120 assert message['To'] == 'angrygrrl@example.org'
121 email_context = template.TEMPLATE_TEST_CONTEXT[
122 'mediagoblin/auth/verification_email.txt']
123 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
125 path = urlparse.urlsplit(email_context['verification_url'])[2]
126 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
127 assert path == u'/auth/verify_email/'
128 parsed_get_params = urlparse.parse_qs(get_params)
130 ## Try verifying with bs verification key, shouldn't work
131 template.clear_test_template_context()
132 response = test_app.get(
133 "/auth/verify_email/?token=total_bs")
134 response.follow()
136 # Correct redirect?
137 assert urlparse.urlsplit(response.location)[2] == '/'
139 # assert context['verification_successful'] == True
140 # TODO: Would be good to test messages here when we can do so...
141 new_user = mg_globals.database.LocalUser.query.filter(
142 LocalUser.username==u'angrygirl'
143 ).first()
144 assert new_user
146 ## Verify the email activation works
147 template.clear_test_template_context()
148 response = test_app.get("%s?%s" % (path, get_params))
149 response.follow()
150 context = template.TEMPLATE_TEST_CONTEXT[
151 'mediagoblin/user_pages/user.html']
152 # assert context['verification_successful'] == True
153 # TODO: Would be good to test messages here when we can do so...
154 new_user = mg_globals.database.LocalUser.query.filter(
155 LocalUser.username==u'angrygirl'
156 ).first()
157 assert new_user
159 # Uniqueness checks
160 # -----------------
161 ## We shouldn't be able to register with that user twice
162 template.clear_test_template_context()
163 response = test_app.post(
164 '/auth/register/', {
165 'username': u'angrygirl',
166 'password': 'iamsoangry2',
167 'email': 'angrygrrl2@example.org'})
169 context = template.TEMPLATE_TEST_CONTEXT[
170 'mediagoblin/auth/register.html']
171 form = context['register_form']
172 assert form.username.errors == [
173 u'Sorry, a user with that name already exists.']
175 ## TODO: Also check for double instances of an email address?
177 ### Oops, forgot the password
178 # -------------------
179 template.clear_test_template_context()
180 response = test_app.post(
181 '/auth/forgot_password/',
182 {'username': u'angrygirl'})
183 response.follow()
185 ## Did we redirect to the proper page? Use the right template?
186 assert urlparse.urlsplit(response.location)[2] == '/auth/login/'
187 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
189 ## Make sure link to change password is sent by email
190 assert len(mail.EMAIL_TEST_INBOX) == 1
191 message = mail.EMAIL_TEST_INBOX.pop()
192 assert message['To'] == 'angrygrrl@example.org'
193 email_context = template.TEMPLATE_TEST_CONTEXT[
194 'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
195 #TODO - change the name of verification_url to something forgot-password-ish
196 assert email_context['verification_url'].encode('ascii') in message.get_payload(decode=True)
198 path = urlparse.urlsplit(email_context['verification_url'])[2]
199 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
200 parsed_get_params = urlparse.parse_qs(get_params)
201 assert path == u'/auth/forgot_password/verify/'
203 ## Try using a bs password-changing verification key, shouldn't work
204 template.clear_test_template_context()
205 response = test_app.get(
206 "/auth/forgot_password/verify/?token=total_bs")
207 response.follow()
209 # Correct redirect?
210 assert urlparse.urlsplit(response.location)[2] == '/'
212 ## Verify step 1 of password-change works -- can see form to change password
213 template.clear_test_template_context()
214 response = test_app.get("%s?%s" % (path, get_params))
215 assert 'mediagoblin/plugins/basic_auth/change_fp.html' in \
216 template.TEMPLATE_TEST_CONTEXT
218 ## Verify step 2.1 of password-change works -- report success to user
219 template.clear_test_template_context()
220 response = test_app.post(
221 '/auth/forgot_password/verify/', {
222 'password': 'iamveryveryangry',
223 'token': parsed_get_params['token']})
224 response.follow()
225 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
227 ## Verify step 2.2 of password-change works -- login w/ new password success
228 template.clear_test_template_context()
229 response = test_app.post(
230 '/auth/login/', {
231 'username': u'angrygirl',
232 'password': 'iamveryveryangry'})
234 # User should be redirected
235 response.follow()
236 assert urlparse.urlsplit(response.location)[2] == '/'
237 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
239 def test_authentication_views(test_app):
241 Test logging in and logging out
243 # Make a new user
244 test_user = fixture_add_user()
247 # Get login
248 # ---------
249 test_app.get('/auth/login/')
250 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
252 # Failed login - blank form
253 # -------------------------
254 template.clear_test_template_context()
255 response = test_app.post('/auth/login/')
256 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
257 form = context['login_form']
258 assert form.username.errors == [u'This field is required.']
260 # Failed login - blank user
261 # -------------------------
262 template.clear_test_template_context()
263 response = test_app.post(
264 '/auth/login/', {
265 'password': u'toast'})
266 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
267 form = context['login_form']
268 assert form.username.errors == [u'This field is required.']
270 # Failed login - blank password
271 # -----------------------------
272 template.clear_test_template_context()
273 response = test_app.post(
274 '/auth/login/', {
275 'username': u'chris'})
276 assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
278 # Failed login - bad user
279 # -----------------------
280 template.clear_test_template_context()
281 response = test_app.post(
282 '/auth/login/', {
283 'username': u'steve',
284 'password': 'toast'})
285 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
286 assert context['login_failed']
288 # Failed login - bad password
289 # ---------------------------
290 template.clear_test_template_context()
291 response = test_app.post(
292 '/auth/login/', {
293 'username': u'chris',
294 'password': 'jam_and_ham'})
295 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
296 assert context['login_failed']
298 # Successful login
299 # ----------------
300 template.clear_test_template_context()
301 response = test_app.post(
302 '/auth/login/', {
303 'username': u'chris',
304 'password': 'toast'})
306 # User should be redirected
307 response.follow()
308 assert urlparse.urlsplit(response.location)[2] == '/'
309 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
311 # Make sure user is in the session
312 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
313 session = context['request'].session
314 assert session['user_id'] == six.text_type(test_user.id)
316 # Successful logout
317 # -----------------
318 template.clear_test_template_context()
319 response = test_app.get('/auth/logout/')
321 # Should be redirected to index page
322 response.follow()
323 assert urlparse.urlsplit(response.location)[2] == '/'
324 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
326 # Make sure the user is not in the session
327 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
328 session = context['request'].session
329 assert 'user_id' not in session
331 # User is redirected to custom URL if POST['next'] is set
332 # -------------------------------------------------------
333 template.clear_test_template_context()
334 response = test_app.post(
335 '/auth/login/', {
336 'username': u'chris',
337 'password': 'toast',
338 'next' : '/u/chris/'})
339 assert urlparse.urlsplit(response.location)[2] == '/u/chris/'
341 ## Verify that username is lowercased on login attempt
342 template.clear_test_template_context()
343 response = test_app.post(
344 '/auth/login/', {
345 'username': u'ANDREW',
346 'password': 'fuselage'})
347 context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
348 form = context['login_form']
350 # Username should no longer be uppercased; it should be lowercased
351 assert not form.username.data == u'ANDREW'
352 assert form.username.data == u'andrew'
354 @pytest.fixture()
355 def authentication_disabled_app(request):
356 return get_app(
357 request,
358 mgoblin_config=pkg_resources.resource_filename(
359 'mediagoblin.tests.auth_configs',
360 'authentication_disabled_appconfig.ini'))
363 def test_authentication_disabled_app(authentication_disabled_app):
364 # app.auth should = false
365 assert mg_globals
366 assert mg_globals.app.auth is False
368 # Try to visit register page
369 template.clear_test_template_context()
370 response = authentication_disabled_app.get('/auth/register/')
371 response.follow()
373 # Correct redirect?
374 assert urlparse.urlsplit(response.location)[2] == '/'
375 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
377 # Try to vist login page
378 template.clear_test_template_context()
379 response = authentication_disabled_app.get('/auth/login/')
380 response.follow()
382 # Correct redirect?
383 assert urlparse.urlsplit(response.location)[2] == '/'
384 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT
386 ## Test check_login_simple should return None
387 assert auth_tools.check_login_simple('test', 'simple') is None
389 # Try to visit the forgot password page
390 template.clear_test_template_context()
391 response = authentication_disabled_app.get('/auth/register/')
392 response.follow()
394 # Correct redirect?
395 assert urlparse.urlsplit(response.location)[2] == '/'
396 assert 'mediagoblin/root.html' in template.TEMPLATE_TEST_CONTEXT