Commit | Line | Data |
---|---|---|
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 | 17 | import urlparse |
4b5f4e87 | 18 | |
1972a888 | 19 | from nose.tools import assert_equal |
4b5f4e87 | 20 | |
1972a888 | 21 | from mediagoblin.auth import lib as auth_lib |
3aa4c668 | 22 | from mediagoblin.tests.tools import setup_fresh_app |
6e7ce8d1 | 23 | from mediagoblin import mg_globals |
460ce564 CAW |
24 | from mediagoblin import util |
25 | ||
4b5f4e87 CAW |
26 | |
27 | ######################## | |
28 | # Test bcrypt auth funcs | |
29 | ######################## | |
30 | ||
31 | def 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 | ||
49 | def 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 |
69 | def 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 | |
238 | def 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 |