# HG changeset patch # User Marcin Kuzminski # Date 2017-03-14 11:40:01 # Node ID 7998d3c5b50d8fe96f630c89141f4e3b7c724501 # Parent 5bdec8f604a5cad78bcaf1c7f427a0b3a2b09dfb pyramid: added checks for password change for authenticated users. diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -18,11 +18,14 @@ # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ +import time import logging from pylons import tmpl_context as c from pyramid.httpexceptions import HTTPFound -from rhodecode.lib.utils2 import StrictAttributeDict +from rhodecode.lib import helpers as h +from rhodecode.lib.utils2 import StrictAttributeDict, safe_int +from rhodecode.model.db import User log = logging.getLogger(__name__) @@ -43,6 +46,34 @@ class BaseAppView(object): self.session = request.session self._rhodecode_user = request.user # auth user self._rhodecode_db_user = self._rhodecode_user.get_instance() + self._maybe_needs_password_change( + request.matched_route.name, self._rhodecode_db_user) + + def _maybe_needs_password_change(self, view_name, user_obj): + log.debug('Checking if user %s needs password change on view %s', + user_obj, view_name) + skip_user_views = [ + 'logout', 'login', + 'my_account_password', 'my_account_password_update' + ] + + if not user_obj: + return + + if user_obj.username == User.DEFAULT_USER: + return + + now = time.time() + should_change = user_obj.user_data.get('force_password_change') + change_after = safe_int(should_change) or 0 + if should_change and now > change_after: + log.debug('User %s requires password change', user_obj) + h.flash('You are required to change your password', 'warning', + ignore_duplicate=True) + + if view_name not in skip_user_views: + raise HTTPFound( + self.request.route_path('my_account_password')) def _get_local_tmpl_context(self): c = TemplateArgs() diff --git a/rhodecode/apps/login/tests/test_register_captcha.py b/rhodecode/apps/login/tests/test_register_captcha.py --- a/rhodecode/apps/login/tests/test_register_captcha.py +++ b/rhodecode/apps/login/tests/test_register_captcha.py @@ -24,6 +24,7 @@ import pytest from rhodecode.apps.login.views import LoginView, CaptchaData from rhodecode.config.routing import ADMIN_PREFIX +from rhodecode.lib.utils2 import AttributeDict from rhodecode.model.settings import SettingsModel from rhodecode.tests.utils import AssertResponse @@ -40,7 +41,7 @@ class RhodeCodeSetting(object): model.create_or_update_setting(name=self.name, val=self.value) return self - def __exit__(self, type, value, traceback): + def __exit__(self, exc_type, exc_val, exc_tb): model = SettingsModel() if self.old_setting: model.create_or_update_setting( @@ -57,8 +58,12 @@ class TestRegisterCaptcha(object): ('privkey', '', CaptchaData(True, 'privkey', '')), ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')), ]) - def test_get_captcha_data(self, private_key, public_key, expected, db): - login_view = LoginView(mock.Mock(), mock.Mock()) + def test_get_captcha_data(self, private_key, public_key, expected, db, + request_stub, user_util): + request_stub.user = user_util.create_user().AuthUser + request_stub.matched_route = AttributeDict({'name': 'login'}) + login_view = LoginView(mock.Mock(), request_stub) + with RhodeCodeSetting('captcha_private_key', private_key): with RhodeCodeSetting('captcha_public_key', public_key): captcha = login_view._get_captcha_data() diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -489,21 +489,12 @@ class BaseController(WSGIController): _route_name) ) - # TODO: Maybe this should be move to pyramid to cover all views. - # check user attributes for password change flag user_obj = auth_user.get_instance() if user_obj and user_obj.user_data.get('force_password_change'): h.flash('You are required to change your password', 'warning', ignore_duplicate=True) - - skip_user_check_urls = [ - 'error.document', 'login.logout', 'login.index', - 'admin/my_account.my_account_password', - 'admin/my_account.my_account_password_update' - ] - if _route_name not in skip_user_check_urls: - return self._dispatch_redirect( - url('my_account_password'), environ, start_response) + return self._dispatch_redirect( + url('my_account_password'), environ, start_response) return WSGIController.__call__(self, environ, start_response)