diff --git a/flask_security/confirmable.py b/flask_security/confirmable.py index 4ef33bd..58ceff7 100644 --- a/flask_security/confirmable.py +++ b/flask_security/confirmable.py @@ -16,7 +16,7 @@ from flask import current_app as app, request, url_for from werkzeug.local import LocalProxy from .exceptions import UserNotFoundError, ConfirmationError -from .utils import send_mail, get_max_age, md5 +from .utils import send_mail, get_max_age, md5, get_message from .signals import user_confirmed, confirm_instructions_sent @@ -87,7 +87,7 @@ def confirm_by_token(token): raise UserNotFoundError() if user.confirmed_at: - raise ConfirmationError('Account has already been confirmed') + raise ConfirmationError(get_message('ALREADY_CONFIRMED')) user.confirmed_at = datetime.utcnow() _datastore._save_model(user) @@ -102,10 +102,10 @@ def confirm_by_token(token): user=_datastore.find_user(id=data[0])) except BadSignature: - raise ConfirmationError('Invalid confirmation token') + raise ConfirmationError(get_message('INVALID_CONFIRMATION_TOKEN')) except UserNotFoundError: - raise ConfirmationError('Invalid confirmation token') + raise ConfirmationError(get_message('INVALID_CONFIRMATION_TOKEN')) def reset_confirmation_token(user): diff --git a/flask_security/core.py b/flask_security/core.py index b12fb1d..c0e65b0 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -60,6 +60,19 @@ _default_config = { 'DEFAULT_HTTP_AUTH_REALM': 'Login Required' } +#: Default Flask-Security flash messages +_default_flash_messages = { + 'UNAUTHORIZED': 'You do not have permission to view this resource.', + 'ACCOUNT_CONFIRMED': 'Your account has been confirmed. You may now log in.', + 'ALREADY_CONFIRMED': 'Your account has already been confirmed', + 'INVALID_CONFIRMATION_TOKEN': 'Invalid confirmation token', + 'PASSWORD_RESET_REQUEST': 'Instructions to reset your password have been sent to %(email)s.', + 'PASSWORD_RESET_EXPIRED': 'You did not reset your password within %(within)s. New instructions have been sent to %(email)s.', + 'INVALID_RESET_PASSWORD_TOKEN': 'Invalid reset password token', + 'CONFIRMATION_REQUEST': 'A new confirmation code has been sent to %(email)s.', + 'CONFIRMATION_EXPIRED': 'You did not confirm your account within %(within)s. New instructions to confirm your account have been sent to %(email)s.' +} + def _user_loader(user_id): try: @@ -233,6 +246,9 @@ class Security(object): for key, value in _default_config.items(): app.config.setdefault('SECURITY_' + key, value) + for key, value in _default_flash_messages.items(): + app.config.setdefault('SECURITY_MSG_' + key, value) + self.datastore = datastore self.auth_provider = AuthenticationProvider() self.login_manager = _get_login_manager(app) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index 7a68f6a..481f075 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -26,7 +26,7 @@ _security = LocalProxy(lambda: current_app.security) _logger = LocalProxy(lambda: current_app.logger) -_default_unauthorized_txt = """ +_default_unauthorized_html = """
The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), @@ -35,14 +35,14 @@ _default_unauthorized_txt = """ def _get_unauthorized_response(text=None, headers=None): - text = text or _default_unauthorized_txt + text = text or _default_unauthorized_html headers = headers or {} - return Response(_default_unauthorized_txt, 401, headers) + return Response(text, 401, headers) def _get_unauthorized_view(): cv = utils.get_url(utils.config_value('UNAUTHORIZED_VIEW')) - utils.do_flash('You do not have permission to view this resource', 'error') + utils.do_flash(utils.get_message('UNAUTHORIZED'), 'error') return redirect(cv or request.referrer or '/') diff --git a/flask_security/recoverable.py b/flask_security/recoverable.py index 0cb70b0..cebd57e 100644 --- a/flask_security/recoverable.py +++ b/flask_security/recoverable.py @@ -16,7 +16,7 @@ from werkzeug.local import LocalProxy from .exceptions import ResetPasswordError, UserNotFoundError from .signals import password_reset, password_reset_requested, \ reset_instructions_sent -from .utils import send_mail, get_max_age, md5 +from .utils import send_mail, get_max_age, md5, get_message # Convenient references @@ -100,10 +100,10 @@ def reset_by_token(token, password): user=_datastore.find_user(id=data[0])) except BadSignature: - raise ResetPasswordError('Invalid reset password token') + raise ResetPasswordError(get_message('INVALID_RESET_PASSWORD_TOKEN')) except UserNotFoundError: - raise ResetPasswordError('Invalid reset password token') + raise ResetPasswordError(get_message('INVALID_RESET_PASSWORD_TOKEN')) def reset_password_reset_token(user): diff --git a/flask_security/utils.py b/flask_security/utils.py index 662563e..9d537c3 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -95,6 +95,10 @@ def get_config(app): return dict([strip_prefix(i) for i in items if i[0].startswith(prefix)]) +def get_message(key, **kwargs): + return config_value('MSG_' + key) % kwargs + + def config_value(key, app=None, default=None): """Get a Flask-Security configuration value. diff --git a/flask_security/views.py b/flask_security/views.py index 74530e0..b398f7d 100644 --- a/flask_security/views.py +++ b/flask_security/views.py @@ -28,7 +28,7 @@ from .recoverable import reset_by_token, \ from .signals import user_registered from .tokens import generate_authentication_token from .utils import get_url, get_post_login_redirect, do_flash, \ - get_remember_token + get_remember_token, get_message # Convenient references @@ -181,7 +181,8 @@ def send_confirmation(): _logger.debug('%s request confirmation instructions' % user) - msg = 'A new confirmation code has been sent to ' + user.email + msg = get_message('CONFIRMATION_REQUEST', email=user.email) + do_flash(msg, 'info') else: @@ -205,15 +206,16 @@ def confirm_account(token): if e.user: reset_confirmation_token(e.user) - msg = ('You did not confirm your email within %s. ' - 'A new confirmation code has been sent to %s' % ( - _security.confirm_email_within, e.user.email)) + + msg = get_message('CONFIRMATION_EXPIRED', + within=_security.confirm_email_within, + email=e.user.email) do_flash(msg, 'error') return redirect(get_url(_security.confirm_error_view)) - do_flash('Your email has been confirmed. You may now log in.', 'success') + do_flash(get_message('ACCOUNT_CONFIRMED'), 'success') return redirect(_security.post_confirm_view or _security.post_login_view) @@ -230,8 +232,8 @@ def forgot_password(): _logger.debug('%s requested to reset their password' % user) - do_flash('Instructions to reset your password have been ' - 'sent to %s' % user.email, 'success') + msg = get_message('PASSWORD_RESET_REQUEST', email=user.email) + do_flash(msg, 'success') return redirect(_security.post_forgot_view) @@ -262,8 +264,11 @@ def reset_password(token): _logger.debug('Password reset error: ' + msg) if e.user: - msg = ('You did not reset your password within ' - '%s.' % _security.reset_password_within) + reset_password_reset_token(e.user) + + msg = get_message('PASSWORD_RESET_EXPIRED', + within=_security.reset_password_within, + email=e.user.email) do_flash(msg, 'error') diff --git a/tests/functional_tests.py b/tests/functional_tests.py index 1f74580..01dfa95 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -49,7 +49,7 @@ class DefaultSecurityTests(SecurityTest): def test_inactive_user(self): r = self.authenticate("tiya@lp.com", "password") - self.assertIn("Inactive user", r.data) + self.assertIn("Account is disabled", r.data) def test_logout(self): self.authenticate() @@ -237,7 +237,7 @@ class ConfirmableTests(SecurityTest): token = registrations[0]['confirm_token'] r = self.client.get('/confirm/' + token, follow_redirects=True) - self.assertIn('Your email has been confirmed. You may now log in.', r.data) + self.assertIn('Your account has been confirmed. You may now log in.', r.data) def test_confirm_email_twice_flashes_already_confirmed_message(self): e = 'dude@lp.com' @@ -249,7 +249,7 @@ class ConfirmableTests(SecurityTest): url = '/confirm/' + token self.client.get(url, follow_redirects=True) r = self.client.get(url, follow_redirects=True) - self.assertIn('Account has already been confirmed', r.data) + self.assertIn('Your account has already been confirmed', r.data) def test_invalid_token_when_confirming_email(self): r = self.client.get('/confirm/bogus', follow_redirects=True) @@ -280,7 +280,7 @@ class ExpiredConfirmationTest(SecurityTest): self.assertNotIn(token, outbox[0].html) expire_text = self.app.security.confirm_email_within - text = 'You did not confirm your email within %s' % expire_text + text = 'You did not confirm your account within %s' % expire_text self.assertIn(text, r.data)