Show More
@@ -0,0 +1,106 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2010-2017 RhodeCode GmbH | |
|
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, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
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 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 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
21 | import pytest | |
|
22 | ||
|
23 | from rhodecode.config.routing import ADMIN_PREFIX | |
|
24 | from rhodecode.tests import ( | |
|
25 | TestController, clear_all_caches, url, | |
|
26 | TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
27 | from rhodecode.tests.fixture import Fixture | |
|
28 | from rhodecode.tests.utils import AssertResponse | |
|
29 | ||
|
30 | fixture = Fixture() | |
|
31 | ||
|
32 | # Hardcode URLs because we don't have a request object to use | |
|
33 | # pyramids URL generation methods. | |
|
34 | index_url = '/' | |
|
35 | login_url = ADMIN_PREFIX + '/login' | |
|
36 | logut_url = ADMIN_PREFIX + '/logout' | |
|
37 | register_url = ADMIN_PREFIX + '/register' | |
|
38 | pwd_reset_url = ADMIN_PREFIX + '/password_reset' | |
|
39 | pwd_reset_confirm_url = ADMIN_PREFIX + '/password_reset_confirmation' | |
|
40 | ||
|
41 | ||
|
42 | class TestPasswordReset(TestController): | |
|
43 | ||
|
44 | @pytest.mark.parametrize( | |
|
45 | 'pwd_reset_setting, show_link, show_reset', [ | |
|
46 | ('hg.password_reset.enabled', True, True), | |
|
47 | ('hg.password_reset.hidden', False, True), | |
|
48 | ('hg.password_reset.disabled', False, False), | |
|
49 | ]) | |
|
50 | def test_password_reset_settings( | |
|
51 | self, pwd_reset_setting, show_link, show_reset): | |
|
52 | clear_all_caches() | |
|
53 | self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
54 | params = { | |
|
55 | 'csrf_token': self.csrf_token, | |
|
56 | 'anonymous': 'True', | |
|
57 | 'default_register': 'hg.register.auto_activate', | |
|
58 | 'default_register_message': '', | |
|
59 | 'default_password_reset': pwd_reset_setting, | |
|
60 | 'default_extern_activate': 'hg.extern_activate.auto', | |
|
61 | } | |
|
62 | resp = self.app.post(url('admin_permissions_application'), params=params) | |
|
63 | self.logout_user() | |
|
64 | ||
|
65 | login_page = self.app.get(login_url) | |
|
66 | asr_login = AssertResponse(login_page) | |
|
67 | index_page = self.app.get(index_url) | |
|
68 | asr_index = AssertResponse(index_page) | |
|
69 | ||
|
70 | if show_link: | |
|
71 | asr_login.one_element_exists('a.pwd_reset') | |
|
72 | asr_index.one_element_exists('a.pwd_reset') | |
|
73 | else: | |
|
74 | asr_login.no_element_exists('a.pwd_reset') | |
|
75 | asr_index.no_element_exists('a.pwd_reset') | |
|
76 | ||
|
77 | response = self.app.get(pwd_reset_url) | |
|
78 | ||
|
79 | assert_response = AssertResponse(response) | |
|
80 | if show_reset: | |
|
81 | response.mustcontain('Send password reset email') | |
|
82 | assert_response.one_element_exists('#email') | |
|
83 | assert_response.one_element_exists('#send') | |
|
84 | else: | |
|
85 | response.mustcontain('Password reset is disabled.') | |
|
86 | assert_response.no_element_exists('#email') | |
|
87 | assert_response.no_element_exists('#send') | |
|
88 | ||
|
89 | def test_password_form_disabled(self): | |
|
90 | self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
91 | params = { | |
|
92 | 'csrf_token': self.csrf_token, | |
|
93 | 'anonymous': 'True', | |
|
94 | 'default_register': 'hg.register.auto_activate', | |
|
95 | 'default_register_message': '', | |
|
96 | 'default_password_reset': 'hg.password_reset.disabled', | |
|
97 | 'default_extern_activate': 'hg.extern_activate.auto', | |
|
98 | } | |
|
99 | self.app.post(url('admin_permissions_application'), params=params) | |
|
100 | self.logout_user() | |
|
101 | ||
|
102 | response = self.app.post( | |
|
103 | pwd_reset_url, {'email': 'lisa@rhodecode.com',} | |
|
104 | ) | |
|
105 | response = response.follow() | |
|
106 | response.mustcontain('Password reset is disabled.') |
@@ -18,6 +18,7 b'' | |||
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | import time | |
|
21 | 22 | import collections |
|
22 | 23 | import datetime |
|
23 | 24 | import formencode |
@@ -37,9 +38,10 b' from rhodecode.lib.auth import (' | |||
|
37 | 38 | from rhodecode.lib.base import get_ip_addr |
|
38 | 39 | from rhodecode.lib.exceptions import UserCreationError |
|
39 | 40 | from rhodecode.lib.utils2 import safe_str |
|
40 | from rhodecode.model.db import User | |
|
41 | from rhodecode.model.db import User, UserApiKeys | |
|
41 | 42 | from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm |
|
42 | 43 | from rhodecode.model.meta import Session |
|
44 | from rhodecode.model.auth_token import AuthTokenModel | |
|
43 | 45 | from rhodecode.model.settings import SettingsModel |
|
44 | 46 | from rhodecode.model.user import UserModel |
|
45 | 47 | from rhodecode.translation import _ |
@@ -289,17 +291,24 b' class LoginView(object):' | |||
|
289 | 291 | 'errors': {}, |
|
290 | 292 | } |
|
291 | 293 | |
|
294 | # always send implicit message to prevent from discovery of | |
|
295 | # matching emails | |
|
296 | msg = _('If such email exists, a password reset link was sent to it.') | |
|
297 | ||
|
292 | 298 | if self.request.POST: |
|
299 | if h.HasPermissionAny('hg.password_reset.disabled')(): | |
|
300 | _email = self.request.POST.get('email', '') | |
|
301 | log.error('Failed attempt to reset password for `%s`.', _email) | |
|
302 | self.session.flash(_('Password reset has been disabled.'), | |
|
303 | queue='error') | |
|
304 | return HTTPFound(self.request.route_path('reset_password')) | |
|
305 | ||
|
293 | 306 | password_reset_form = PasswordResetForm()() |
|
294 | 307 | try: |
|
295 | 308 | form_result = password_reset_form.to_python( |
|
296 | 309 | self.request.params) |
|
297 | if h.HasPermissionAny('hg.password_reset.disabled')(): | |
|
298 | log.error('Failed attempt to reset password for %s.', form_result['email'] ) | |
|
299 | self.session.flash( | |
|
300 | _('Password reset has been disabled.'), | |
|
301 | queue='error') | |
|
302 | return HTTPFound(self.request.route_path('reset_password')) | |
|
310 | user_email = form_result['email'] | |
|
311 | ||
|
303 | 312 | if captcha.active: |
|
304 | 313 | response = submit( |
|
305 | 314 | self.request.params.get('recaptcha_challenge_field'), |
@@ -310,43 +319,66 b' class LoginView(object):' | |||
|
310 | 319 | _value = form_result |
|
311 | 320 | _msg = _('Bad captcha') |
|
312 | 321 | error_dict = {'recaptcha_field': _msg} |
|
313 |
raise formencode.Invalid( |
|
|
314 |
|
|
|
322 | raise formencode.Invalid( | |
|
323 | _msg, _value, None, error_dict=error_dict) | |
|
324 | # Generate reset URL and send mail. | |
|
325 | user = User.get_by_email(user_email) | |
|
315 | 326 | |
|
316 | # Generate reset URL and send mail. | |
|
317 | user_email = form_result['email'] | |
|
318 | user = User.get_by_email(user_email) | |
|
327 | # generate password reset token that expires in 10minutes | |
|
328 | desc = 'Generated token for password reset from {}'.format( | |
|
329 | datetime.datetime.now().isoformat()) | |
|
330 | reset_token = AuthTokenModel().create( | |
|
331 | user, lifetime=10, | |
|
332 | description=desc, | |
|
333 | role=UserApiKeys.ROLE_PASSWORD_RESET) | |
|
334 | Session().commit() | |
|
335 | ||
|
336 | log.debug('Successfully created password recovery token') | |
|
319 | 337 | password_reset_url = self.request.route_url( |
|
320 | 338 | 'reset_password_confirmation', |
|
321 |
_query={'key': |
|
|
339 | _query={'key': reset_token.api_key}) | |
|
322 | 340 | UserModel().reset_password_link( |
|
323 | 341 | form_result, password_reset_url) |
|
324 | ||
|
325 | 342 | # Display success message and redirect. |
|
326 | self.session.flash( | |
|
327 | _('Your password reset link was sent'), | |
|
328 | queue='success') | |
|
329 | return HTTPFound(self.request.route_path('login')) | |
|
343 | self.session.flash(msg, queue='success') | |
|
344 | return HTTPFound(self.request.route_path('reset_password')) | |
|
330 | 345 | |
|
331 | 346 | except formencode.Invalid as errors: |
|
332 | 347 | render_ctx.update({ |
|
333 | 348 | 'defaults': errors.value, |
|
334 | 'errors': errors.error_dict, | |
|
335 | 349 | }) |
|
350 | log.debug('faking response on invalid password reset') | |
|
351 | # make this take 2s, to prevent brute forcing. | |
|
352 | time.sleep(2) | |
|
353 | self.session.flash(msg, queue='success') | |
|
354 | return HTTPFound(self.request.route_path('reset_password')) | |
|
336 | 355 | |
|
337 | 356 | return render_ctx |
|
338 | 357 | |
|
339 | 358 | @view_config(route_name='reset_password_confirmation', |
|
340 | 359 | request_method='GET') |
|
341 | 360 | def password_reset_confirmation(self): |
|
361 | ||
|
342 | 362 | if self.request.GET and self.request.GET.get('key'): |
|
363 | # make this take 2s, to prevent brute forcing. | |
|
364 | time.sleep(2) | |
|
365 | ||
|
366 | token = AuthTokenModel().get_auth_token( | |
|
367 | self.request.GET.get('key')) | |
|
368 | ||
|
369 | # verify token is the correct role | |
|
370 | if token is None or token.role != UserApiKeys.ROLE_PASSWORD_RESET: | |
|
371 | log.debug('Got token with role:%s expected is %s', | |
|
372 | getattr(token, 'role', 'EMPTY_TOKEN'), | |
|
373 | UserApiKeys.ROLE_PASSWORD_RESET) | |
|
374 | self.session.flash( | |
|
375 | _('Given reset token is invalid'), queue='error') | |
|
376 | return HTTPFound(self.request.route_path('reset_password')) | |
|
377 | ||
|
343 | 378 | try: |
|
344 | user = User.get_by_auth_token(self.request.GET.get('key')) | |
|
345 | password_reset_url = self.request.route_url( | |
|
346 |
|
|
|
347 | _query={'key': user.api_key}) | |
|
348 | data = {'email': user.email} | |
|
349 | UserModel().reset_password(data, password_reset_url) | |
|
379 | owner = token.user | |
|
380 | data = {'email': owner.email, 'token': token.api_key} | |
|
381 | UserModel().reset_password(data) | |
|
350 | 382 | self.session.flash( |
|
351 | 383 | _('Your password reset was successful, ' |
|
352 | 384 | 'a new password has been sent to your email'), |
@@ -41,7 +41,7 b' class AuthTokenModel(BaseModel):' | |||
|
41 | 41 | """ |
|
42 | 42 | :param user: user or user_id |
|
43 | 43 | :param description: description of ApiKey |
|
44 |
:param lifetime: expiration time in |
|
|
44 | :param lifetime: expiration time in minutes | |
|
45 | 45 | :param role: role for the apikey |
|
46 | 46 | """ |
|
47 | 47 | from rhodecode.lib.auth import generate_auth_token |
@@ -85,3 +85,13 b' class AuthTokenModel(BaseModel):' | |||
|
85 | 85 | .filter(or_(UserApiKeys.expires == -1, |
|
86 | 86 | UserApiKeys.expires >= time.time())) |
|
87 | 87 | return user_auth_tokens |
|
88 | ||
|
89 | def get_auth_token(self, auth_token): | |
|
90 | auth_token = UserApiKeys.query().filter( | |
|
91 | UserApiKeys.api_key == auth_token) | |
|
92 | auth_token = auth_token \ | |
|
93 | .filter(or_(UserApiKeys.expires == -1, | |
|
94 | UserApiKeys.expires >= time.time()))\ | |
|
95 | .first() | |
|
96 | ||
|
97 | return auth_token |
@@ -943,6 +943,8 b' class UserApiKeys(Base, BaseModel):' | |||
|
943 | 943 | ROLE_VCS = 'token_role_vcs' |
|
944 | 944 | ROLE_API = 'token_role_api' |
|
945 | 945 | ROLE_FEED = 'token_role_feed' |
|
946 | ROLE_PASSWORD_RESET = 'token_password_reset' | |
|
947 | ||
|
946 | 948 | ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED] |
|
947 | 949 | |
|
948 | 950 | user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) |
@@ -538,7 +538,7 b' class UserModel(BaseModel):' | |||
|
538 | 538 | |
|
539 | 539 | return True |
|
540 | 540 | |
|
541 |
def reset_password(self, data |
|
|
541 | def reset_password(self, data): | |
|
542 | 542 | from rhodecode.lib.celerylib import tasks, run_task |
|
543 | 543 | from rhodecode.model.notification import EmailNotificationModel |
|
544 | 544 | from rhodecode.lib import auth |
@@ -554,8 +554,15 b' class UserModel(BaseModel):' | |||
|
554 | 554 | user.update_userdata(force_password_change=True) |
|
555 | 555 | |
|
556 | 556 | Session().add(user) |
|
557 | ||
|
558 | # now delete the token in question | |
|
559 | UserApiKeys = AuthTokenModel.cls | |
|
560 | UserApiKeys().query().filter( | |
|
561 | UserApiKeys.api_key == data['token']).delete() | |
|
562 | ||
|
557 | 563 | Session().commit() |
|
558 | 564 | log.info('successfully reset password for `%s`', user_email) |
|
565 | ||
|
559 | 566 | if new_passwd is None: |
|
560 | 567 | raise Exception('unable to generate new password') |
|
561 | 568 | |
@@ -563,7 +570,6 b' class UserModel(BaseModel):' | |||
|
563 | 570 | |
|
564 | 571 | email_kwargs = { |
|
565 | 572 | 'new_password': new_passwd, |
|
566 | 'password_reset_url': pwd_reset_url, | |
|
567 | 573 | 'user': user, |
|
568 | 574 | 'email': user_email, |
|
569 | 575 | 'date': datetime.datetime.now() |
@@ -571,7 +577,8 b' class UserModel(BaseModel):' | |||
|
571 | 577 | |
|
572 | 578 | (subject, headers, email_body, |
|
573 | 579 | email_body_plaintext) = EmailNotificationModel().render_email( |
|
574 |
EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION, |
|
|
580 | EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION, | |
|
581 | **email_kwargs) | |
|
575 | 582 | |
|
576 | 583 | recipients = [user_email] |
|
577 | 584 |
@@ -16,6 +16,7 b' There was a request to reset your passwo' | |||
|
16 | 16 | You can continue, and generate new password by clicking following URL: |
|
17 | 17 | ${password_reset_url} |
|
18 | 18 | |
|
19 | This link will be active for 10 minutes. | |
|
19 | 20 | ${self.plaintext_footer()} |
|
20 | 21 | </%def> |
|
21 | 22 | |
@@ -28,4 +29,5 b' There was a request to reset your passwo' | |||
|
28 | 29 | <strong>If you did not request a password reset, please contact your RhodeCode administrator.</strong> |
|
29 | 30 | </p><p> |
|
30 | 31 | <a href="${password_reset_url}">${_('Generate new password here')}.</a> |
|
32 | This link will be active for 10 minutes. | |
|
31 | 33 | </p> |
@@ -9,12 +9,11 b' Your new RhodeCode password' | |||
|
9 | 9 | <%def name="body_plaintext()" filter="n,trim"> |
|
10 | 10 | Hi ${user.username}, |
|
11 | 11 | |
|
12 | There was a request to reset your password using the email address ${email} on ${h.format_date(date)} | |
|
12 | Below is your new access password for RhodeCode. | |
|
13 | 13 | |
|
14 | 14 | *If you didn't do this, please contact your RhodeCode administrator.* |
|
15 | 15 | |
|
16 | You can continue, and generate new password by clicking following URL: | |
|
17 | ${password_reset_url} | |
|
16 | password: ${new_password} | |
|
18 | 17 | |
|
19 | 18 | ${self.plaintext_footer()} |
|
20 | 19 | </%def> |
@@ -27,4 +26,4 b' Below is your new access password for Rh' | |||
|
27 | 26 | <br/> |
|
28 | 27 | <strong>If you didn't request a new password, please contact your RhodeCode administrator.</strong> |
|
29 | 28 | </p> |
|
30 |
<p>password: < |
|
|
29 | <p>password: <pre>${new_password}</pre> |
@@ -25,15 +25,13 b' import pytest' | |||
|
25 | 25 | |
|
26 | 26 | from rhodecode.config.routing import ADMIN_PREFIX |
|
27 | 27 | from rhodecode.tests import ( |
|
28 | TestController, assert_session_flash, clear_all_caches, url, | |
|
29 | HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
28 | assert_session_flash, url, HG_REPO, TEST_USER_ADMIN_LOGIN) | |
|
30 | 29 | from rhodecode.tests.fixture import Fixture |
|
31 | 30 | from rhodecode.tests.utils import AssertResponse, get_session_from_response |
|
32 |
from rhodecode.lib.auth import check_password |
|
|
33 | from rhodecode.lib import helpers as h | |
|
31 | from rhodecode.lib.auth import check_password | |
|
34 | 32 | from rhodecode.model.auth_token import AuthTokenModel |
|
35 | 33 | from rhodecode.model import validators |
|
36 | from rhodecode.model.db import User, Notification | |
|
34 | from rhodecode.model.db import User, Notification, UserApiKeys | |
|
37 | 35 | from rhodecode.model.meta import Session |
|
38 | 36 | |
|
39 | 37 | fixture = Fixture() |
@@ -49,7 +47,7 b" pwd_reset_confirm_url = ADMIN_PREFIX + '" | |||
|
49 | 47 | |
|
50 | 48 | |
|
51 | 49 | @pytest.mark.usefixtures('app') |
|
52 | class TestLoginController: | |
|
50 | class TestLoginController(object): | |
|
53 | 51 | destroy_users = set() |
|
54 | 52 | |
|
55 | 53 | @classmethod |
@@ -374,54 +372,42 b' class TestLoginController:' | |||
|
374 | 372 | def test_forgot_password_wrong_mail(self): |
|
375 | 373 | bad_email = 'marcin@wrongmail.org' |
|
376 | 374 | response = self.app.post( |
|
377 | pwd_reset_url, | |
|
378 | {'email': bad_email, } | |
|
375 | pwd_reset_url, {'email': bad_email, } | |
|
379 | 376 | ) |
|
377 | assert_session_flash(response, | |
|
378 | 'If such email exists, a password reset link was sent to it.') | |
|
380 | 379 | |
|
381 | msg = validators.ValidSystemEmail()._messages['non_existing_email'] | |
|
382 | msg = h.html_escape(msg % {'email': bad_email}) | |
|
383 | response.mustcontain() | |
|
384 | ||
|
385 | def test_forgot_password(self): | |
|
380 | def test_forgot_password(self, user_util): | |
|
386 | 381 | response = self.app.get(pwd_reset_url) |
|
387 | 382 | assert response.status == '200 OK' |
|
388 | 383 | |
|
389 | username = 'test_password_reset_1' | |
|
390 | password = 'qweqwe' | |
|
391 | email = 'marcin@python-works.com' | |
|
392 | name = 'passwd' | |
|
393 | lastname = 'reset' | |
|
384 | user = user_util.create_user() | |
|
385 | user_id = user.user_id | |
|
386 | email = user.email | |
|
394 | 387 | |
|
395 | new = User() | |
|
396 | new.username = username | |
|
397 | new.password = password | |
|
398 | new.email = email | |
|
399 | new.name = name | |
|
400 | new.lastname = lastname | |
|
401 | new.api_key = generate_auth_token(username) | |
|
402 | Session().add(new) | |
|
403 | Session().commit() | |
|
388 | response = self.app.post(pwd_reset_url, {'email': email, }) | |
|
404 | 389 | |
|
405 | response = self.app.post(pwd_reset_url, | |
|
406 | {'email': email, }) | |
|
407 | ||
|
408 | assert_session_flash( | |
|
409 | response, 'Your password reset link was sent') | |
|
410 | ||
|
411 | response = response.follow() | |
|
390 | assert_session_flash(response, | |
|
391 | 'If such email exists, a password reset link was sent to it.') | |
|
412 | 392 | |
|
413 | 393 | # BAD KEY |
|
414 | ||
|
415 | key = "bad" | |
|
416 | confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, key) | |
|
394 | confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, 'badkey') | |
|
417 | 395 | response = self.app.get(confirm_url) |
|
418 | 396 | assert response.status == '302 Found' |
|
419 | 397 | assert response.location.endswith(pwd_reset_url) |
|
398 | assert_session_flash(response, 'Given reset token is invalid') | |
|
399 | ||
|
400 | response.follow() # cleanup flash | |
|
420 | 401 | |
|
421 | 402 | # GOOD KEY |
|
403 | key = UserApiKeys.query()\ | |
|
404 | .filter(UserApiKeys.user_id == user_id)\ | |
|
405 | .filter(UserApiKeys.role == UserApiKeys.ROLE_PASSWORD_RESET)\ | |
|
406 | .first() | |
|
422 | 407 | |
|
423 | key = User.get_by_username(username).api_key | |
|
424 | confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, key) | |
|
408 | assert key | |
|
409 | ||
|
410 | confirm_url = '{}?key={}'.format(pwd_reset_confirm_url, key.api_key) | |
|
425 | 411 | response = self.app.get(confirm_url) |
|
426 | 412 | assert response.status == '302 Found' |
|
427 | 413 | assert response.location.endswith(login_url) |
@@ -431,7 +417,7 b' class TestLoginController:' | |||
|
431 | 417 | 'Your password reset was successful, ' |
|
432 | 418 | 'a new password has been sent to your email') |
|
433 | 419 | |
|
434 |
|
|
|
420 | response.follow() | |
|
435 | 421 | |
|
436 | 422 | def _get_api_whitelist(self, values=None): |
|
437 | 423 | config = {'api_access_controllers_whitelist': values or []} |
@@ -522,70 +508,3 b' class TestLoginController:' | |||
|
522 | 508 | repo_name=HG_REPO, revision='tip', |
|
523 | 509 | api_key=new_auth_token.api_key), |
|
524 | 510 | status=302) |
|
525 | ||
|
526 | ||
|
527 | class TestPasswordReset(TestController): | |
|
528 | ||
|
529 | @pytest.mark.parametrize( | |
|
530 | 'pwd_reset_setting, show_link, show_reset', [ | |
|
531 | ('hg.password_reset.enabled', True, True), | |
|
532 | ('hg.password_reset.hidden', False, True), | |
|
533 | ('hg.password_reset.disabled', False, False), | |
|
534 | ]) | |
|
535 | def test_password_reset_settings( | |
|
536 | self, pwd_reset_setting, show_link, show_reset): | |
|
537 | clear_all_caches() | |
|
538 | self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
539 | params = { | |
|
540 | 'csrf_token': self.csrf_token, | |
|
541 | 'anonymous': 'True', | |
|
542 | 'default_register': 'hg.register.auto_activate', | |
|
543 | 'default_register_message': '', | |
|
544 | 'default_password_reset': pwd_reset_setting, | |
|
545 | 'default_extern_activate': 'hg.extern_activate.auto', | |
|
546 | } | |
|
547 | resp = self.app.post(url('admin_permissions_application'), params=params) | |
|
548 | self.logout_user() | |
|
549 | ||
|
550 | login_page = self.app.get(login_url) | |
|
551 | asr_login = AssertResponse(login_page) | |
|
552 | index_page = self.app.get(index_url) | |
|
553 | asr_index = AssertResponse(index_page) | |
|
554 | ||
|
555 | if show_link: | |
|
556 | asr_login.one_element_exists('a.pwd_reset') | |
|
557 | asr_index.one_element_exists('a.pwd_reset') | |
|
558 | else: | |
|
559 | asr_login.no_element_exists('a.pwd_reset') | |
|
560 | asr_index.no_element_exists('a.pwd_reset') | |
|
561 | ||
|
562 | pwdreset_page = self.app.get(pwd_reset_url) | |
|
563 | ||
|
564 | asr_reset = AssertResponse(pwdreset_page) | |
|
565 | if show_reset: | |
|
566 | assert 'Send password reset email' in pwdreset_page | |
|
567 | asr_reset.one_element_exists('#email') | |
|
568 | asr_reset.one_element_exists('#send') | |
|
569 | else: | |
|
570 | assert 'Password reset is disabled.' in pwdreset_page | |
|
571 | asr_reset.no_element_exists('#email') | |
|
572 | asr_reset.no_element_exists('#send') | |
|
573 | ||
|
574 | def test_password_form_disabled(self): | |
|
575 | self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |
|
576 | params = { | |
|
577 | 'csrf_token': self.csrf_token, | |
|
578 | 'anonymous': 'True', | |
|
579 | 'default_register': 'hg.register.auto_activate', | |
|
580 | 'default_register_message': '', | |
|
581 | 'default_password_reset': 'hg.password_reset.disabled', | |
|
582 | 'default_extern_activate': 'hg.extern_activate.auto', | |
|
583 | } | |
|
584 | self.app.post(url('admin_permissions_application'), params=params) | |
|
585 | self.logout_user() | |
|
586 | ||
|
587 | pwdreset_page = self.app.post( | |
|
588 | pwd_reset_url, | |
|
589 | {'email': 'lisa@rhodecode.com',} | |
|
590 | ) | |
|
591 | assert 'Password reset is disabled.' in pwdreset_page |
General Comments 0
You need to be logged in to leave comments.
Login now