Merge remote branch 'remotes/gullydwarf-cfdv/f360_tagging' into mergetags
[mediagoblin.git] / mediagoblin / tests / test_auth.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
4b5f4e87
CAW
2# Copyright (C) 2011 Free Software Foundation, Inc
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
1972a888 17import urlparse
4b5f4e87 18
1972a888 19from nose.tools import assert_equal
4b5f4e87 20
1972a888 21from mediagoblin.auth import lib as auth_lib
3aa4c668 22from mediagoblin.tests.tools import setup_fresh_app
6e7ce8d1 23from mediagoblin import mg_globals
460ce564
CAW
24from mediagoblin import util
25
4b5f4e87
CAW
26
27########################
28# Test bcrypt auth funcs
29########################
30
31def test_bcrypt_check_password():
32 # Check known 'lollerskates' password against check function
33 assert auth_lib.bcrypt_check_password(
34 'lollerskates',
35 '$2a$12$PXU03zfrVCujBhVeICTwtOaHTUs5FFwsscvSSTJkqx/2RQ0Lhy/nO')
36
db780024
CAW
37 assert not auth_lib.bcrypt_check_password(
38 'notthepassword',
39 '$2a$12$PXU03zfrVCujBhVeICTwtOaHTUs5FFwsscvSSTJkqx/2RQ0Lhy/nO')
40
41
4b5f4e87 42 # Same thing, but with extra fake salt.
db780024
CAW
43 assert not auth_lib.bcrypt_check_password(
44 'notthepassword',
4b5f4e87
CAW
45 '$2a$12$ELVlnw3z1FMu6CEGs/L8XO8vl0BuWSlUHgh0rUrry9DUXGMUNWwl6',
46 '3><7R45417')
47
48
49def test_bcrypt_gen_password_hash():
50 pw = 'youwillneverguessthis'
51
52 # Normal password hash generation, and check on that hash
53 hashed_pw = auth_lib.bcrypt_gen_password_hash(pw)
54 assert auth_lib.bcrypt_check_password(
55 pw, hashed_pw)
db780024
CAW
56 assert not auth_lib.bcrypt_check_password(
57 'notthepassword', hashed_pw)
58
4b5f4e87
CAW
59
60 # Same thing, extra salt.
61 hashed_pw = auth_lib.bcrypt_gen_password_hash(pw, '3><7R45417')
62 assert auth_lib.bcrypt_check_password(
63 pw, hashed_pw, '3><7R45417')
db780024
CAW
64 assert not auth_lib.bcrypt_check_password(
65 'notthepassword', hashed_pw, '3><7R45417')
460ce564
CAW
66
67
3aa4c668
CAW
68@setup_fresh_app
69def test_register_views(test_app):
2fecc29d
CAW
70 """
71 Massive test function that all our registration-related views all work.
72 """
460ce564 73 # Test doing a simple GET on the page
651403f0
CAW
74 # -----------------------------------
75
460ce564
CAW
76 test_app.get('/auth/register/')
77 # Make sure it rendered with the appropriate template
78 assert util.TEMPLATE_TEST_CONTEXT.has_key(
79 'mediagoblin/auth/register.html')
757690cc 80
460ce564 81 # Try to register without providing anything, should error
651403f0
CAW
82 # --------------------------------------------------------
83
460ce564
CAW
84 util.clear_test_template_context()
85 test_app.post(
86 '/auth/register/', {})
87 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
88 form = context['register_form']
89 assert form.username.errors == [u'This field is required.']
90 assert form.password.errors == [u'This field is required.']
91 assert form.confirm_password.errors == [u'This field is required.']
92 assert form.email.errors == [u'This field is required.']
651403f0
CAW
93
94 # Try to register with fields that are known to be invalid
95 # --------------------------------------------------------
96
97 ## too short
98 util.clear_test_template_context()
99 test_app.post(
100 '/auth/register/', {
101 'username': 'l',
102 'password': 'o',
103 'confirm_password': 'o',
104 'email': 'l'})
105 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
106 form = context['register_form']
107
108 assert form.username.errors == [
109 u'Field must be between 3 and 30 characters long.']
110 assert form.password.errors == [
111 u'Field must be between 6 and 30 characters long.']
112
113 ## bad form
114 util.clear_test_template_context()
115 test_app.post(
116 '/auth/register/', {
117 'username': '@_@',
118 'email': 'lollerskates'})
119 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
120 form = context['register_form']
121
122 assert form.username.errors == [
123 u'Invalid input.']
124 assert form.email.errors == [
125 u'Invalid email address.']
126
127 ## mismatching passwords
128 util.clear_test_template_context()
129 test_app.post(
130 '/auth/register/', {
131 'password': 'herpderp',
132 'confirm_password': 'derpherp'})
133 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html']
134 form = context['register_form']
135
136 assert form.password.errors == [
137 u'Passwords must match.']
138
139 ## At this point there should be no users in the database ;)
6e7ce8d1 140 assert not mg_globals.database.User.find().count()
651403f0
CAW
141
142 # Successful register
143 # -------------------
1972a888
CAW
144 util.clear_test_template_context()
145 response = test_app.post(
146 '/auth/register/', {
147 'username': 'happygirl',
148 'password': 'iamsohappy',
149 'confirm_password': 'iamsohappy',
150 'email': 'happygrrl@example.org'})
151 response.follow()
152
651403f0 153 ## Did we redirect to the proper page? Use the right template?
1972a888
CAW
154 assert_equal(
155 urlparse.urlsplit(response.location)[2],
0bc03620 156 '/u/happygirl/')
1972a888 157 assert util.TEMPLATE_TEST_CONTEXT.has_key(
0bc03620 158 'mediagoblin/user_pages/user.html')
1972a888 159
651403f0 160 ## Make sure user is in place
6e7ce8d1 161 new_user = mg_globals.database.User.find_one(
1972a888
CAW
162 {'username': 'happygirl'})
163 assert new_user
164 assert new_user['status'] == u'needs_email_verification'
165 assert new_user['email_verified'] == False
166
f73f4c4b
CAW
167 ## Make sure user is logged in
168 request = util.TEMPLATE_TEST_CONTEXT[
169 'mediagoblin/user_pages/user.html']['request']
170 assert request.session['user_id'] == unicode(new_user['_id'])
171
1972a888
CAW
172 ## Make sure we get email confirmation, and try verifying
173 assert len(util.EMAIL_TEST_INBOX) == 1
174 message = util.EMAIL_TEST_INBOX.pop()
175 assert message['To'] == 'happygrrl@example.org'
176 email_context = util.TEMPLATE_TEST_CONTEXT[
177 'mediagoblin/auth/verification_email.txt']
178 assert email_context['verification_url'] in message.get_payload(decode=True)
179
180 path = urlparse.urlsplit(email_context['verification_url'])[2]
181 get_params = urlparse.urlsplit(email_context['verification_url'])[3]
182 assert path == u'/auth/verify_email/'
183 parsed_get_params = urlparse.parse_qs(get_params)
184
185 ### user should have these same parameters
186 assert parsed_get_params['userid'] == [
187 unicode(new_user['_id'])]
188 assert parsed_get_params['token'] == [
189 new_user['verification_key']]
757690cc 190
7b1e17ed
CAW
191 ## Try verifying with bs verification key, shouldn't work
192 util.clear_test_template_context()
193 test_app.get(
194 "/auth/verify_email/?userid=%s&token=total_bs" % unicode(
195 new_user['_id']))
196 context = util.TEMPLATE_TEST_CONTEXT[
e054ae9b 197 'mediagoblin/user_pages/user.html']
7b1e17ed 198 assert context['verification_successful'] == False
6e7ce8d1 199 new_user = mg_globals.database.User.find_one(
7b1e17ed
CAW
200 {'username': 'happygirl'})
201 assert new_user
202 assert new_user['status'] == u'needs_email_verification'
203 assert new_user['email_verified'] == False
204
205 ## Verify the email activation works
206 util.clear_test_template_context()
207 test_app.get("%s?%s" % (path, get_params))
208 context = util.TEMPLATE_TEST_CONTEXT[
e054ae9b 209 'mediagoblin/user_pages/user.html']
7b1e17ed 210 assert context['verification_successful'] == True
6e7ce8d1 211 new_user = mg_globals.database.User.find_one(
7b1e17ed
CAW
212 {'username': 'happygirl'})
213 assert new_user
214 assert new_user['status'] == u'active'
215 assert new_user['email_verified'] == True
1972a888 216
cb9bac0c
CAW
217 # Uniqueness checks
218 # -----------------
219 ## We shouldn't be able to register with that user twice
8a869db8
CAW
220 util.clear_test_template_context()
221 response = test_app.post(
222 '/auth/register/', {
223 'username': 'happygirl',
224 'password': 'iamsohappy2',
225 'confirm_password': 'iamsohappy2',
226 'email': 'happygrrl2@example.org'})
757690cc 227
8a869db8
CAW
228 context = util.TEMPLATE_TEST_CONTEXT[
229 'mediagoblin/auth/register.html']
230 form = context['register_form']
231 assert form.username.errors == [
232 u'Sorry, a user with that name already exists.']
651403f0 233
8a869db8 234 ## TODO: Also check for double instances of an email address?
757690cc
CM
235
236
237@setup_fresh_app
238def test_authentication_views(test_app):
239 """
240 Test logging in and logging out
241 """
242 # Make a new user
243 test_user = mg_globals.database.User()
244 test_user['username'] = u'chris'
245 test_user['email'] = u'chris@example.com'
246 test_user['pw_hash'] = auth_lib.bcrypt_gen_password_hash('toast')
247 test_user.save()
248
249 # Get login
0a4cecdc 250 # ---------
757690cc 251 test_app.get('/auth/login/')
757690cc
CM
252 assert util.TEMPLATE_TEST_CONTEXT.has_key(
253 'mediagoblin/auth/login.html')
254
0a4cecdc
CM
255 # Failed login - blank form
256 # -------------------------
257 util.clear_test_template_context()
258 response = test_app.post('/auth/login/')
259 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
260 form = context['login_form']
261 assert form.username.errors == [u'This field is required.']
262 assert form.password.errors == [u'This field is required.']
263
264 # Failed login - blank user
265 # -------------------------
266 util.clear_test_template_context()
267 response = test_app.post(
268 '/auth/login/', {
269 'password': u'toast'})
270 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
271 form = context['login_form']
272 assert form.username.errors == [u'This field is required.']
273
274 # Failed login - blank password
275 # -----------------------------
276 util.clear_test_template_context()
277 response = test_app.post(
278 '/auth/login/', {
279 'username': u'chris'})
280 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
281 form = context['login_form']
282 assert form.password.errors == [u'This field is required.']
283
284 # Failed login - bad user
285 # -----------------------
286 util.clear_test_template_context()
287 response = test_app.post(
288 '/auth/login/', {
289 'username': u'steve',
290 'password': 'toast'})
291 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
292 assert context['login_failed']
293
294 # Failed login - bad password
295 # ---------------------------
296 util.clear_test_template_context()
297 response = test_app.post(
298 '/auth/login/', {
299 'username': u'chris',
300 'password': 'jam'})
301 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/login.html']
302 assert context['login_failed']
303
304 # Successful login
305 # ----------------
757690cc
CM
306 util.clear_test_template_context()
307 response = test_app.post(
308 '/auth/login/', {
309 'username': u'chris',
310 'password': 'toast'})
0a4cecdc
CM
311
312 # User should be redirected
757690cc
CM
313 response.follow()
314 assert_equal(
315 urlparse.urlsplit(response.location)[2],
316 '/')
317 assert util.TEMPLATE_TEST_CONTEXT.has_key(
318 'mediagoblin/root.html')
319
0a4cecdc
CM
320 # Make sure user is in the session
321 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
322 session = context['request'].session
757690cc
CM
323 assert session['user_id'] == unicode(test_user['_id'])
324
0a4cecdc
CM
325 # Successful logout
326 # -----------------
327 util.clear_test_template_context()
328 response = test_app.get('/auth/logout/')
329
330 # Should be redirected to index page
331 response.follow()
332 assert_equal(
333 urlparse.urlsplit(response.location)[2],
334 '/')
335 assert util.TEMPLATE_TEST_CONTEXT.has_key(
336 'mediagoblin/root.html')
337
338 # Make sure the user is not in the session
339 context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/root.html']
340 session = context['request'].session
341 assert session.has_key('user_id') == False
757690cc 342
12c231c8
CM
343 # User is redirected to custom URL if POST['next'] is set
344 # -------------------------------------------------------
345 util.clear_test_template_context()
346 response = test_app.post(
347 '/auth/login/', {
348 'username': u'chris',
349 'password': 'toast',
350 'next' : '/u/chris/'})
351 assert_equal(
352 urlparse.urlsplit(response.location)[2],
353 '/u/chris/')
354