From 75c10c75d9d01b154f486cc0a5ce4f1913363e8a Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Tue, 29 Jan 2013 19:13:20 -0500 Subject: [PATCH 1/7] Fixed typo in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e1a4faf..4cf7702 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ Flask-Login==0.1 Flask-Principal==0.2 Flask-Script==0.3.2 Flask-WTF==0.5.4 -passlib=1.5.3 +passlib==1.5.3 From cf37d59d47af38106db1eaf0d816db4e36152be1 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Tue, 29 Jan 2013 19:27:05 -0500 Subject: [PATCH 2/7] Updated requirements to reflect packages in setup.py --- requirements.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cf7702..a87ae62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ Flask==0.8 -Flask-Login==0.1 -Flask-Principal==0.2 -Flask-Script==0.3.2 -Flask-WTF==0.5.4 -passlib==1.5.3 +Flask-Login==0.1.3 +Flask-Mail>=0.7.3 +Flask-Principal>=0.3.3 +Flask-Script==0.5.3 +Flask-WTF>=0.8 +itsdangerous>=0.17 +passlib==1.6.1 From 3f243d1c11dd7523303496c532b69bbf655ffd66 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Tue, 29 Jan 2013 19:51:25 -0500 Subject: [PATCH 3/7] Updated flask version and added flask-script to setup.py. --- requirements.txt | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index a87ae62..8307c2d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Flask==0.8 +Flask>=0.9 Flask-Login==0.1.3 Flask-Mail>=0.7.3 Flask-Principal>=0.3.3 diff --git a/setup.py b/setup.py index cfead93..ea11896 100644 --- a/setup.py +++ b/setup.py @@ -34,10 +34,11 @@ setup( include_package_data=True, platforms='any', install_requires=[ - 'Flask>=0.8', + 'Flask>=0.9', 'Flask-Login>=0.1.3', 'Flask-Mail>=0.7.3', 'Flask-Principal>=0.3.3', + 'Flask-Script==0.5.3', 'Flask-WTF>=0.8', 'itsdangerous>=0.17', 'passlib>=1.6.1', From 02c49ee423d15c058c6815e0065cf1b91f25f082 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Tue, 29 Jan 2013 22:24:11 -0500 Subject: [PATCH 4/7] Paths for templates are now configurable --- docs/configuration.rst | 29 ++++++++ docs/customizing.rst | 4 +- flask_security/core.py | 6 ++ flask_security/views.py | 15 ++-- tests/configured_tests.py | 73 +++++++++++++++++++ .../custom_security/forgot_password.html | 1 + .../templates/custom_security/login_user.html | 1 + .../custom_security/register_user.html | 1 + .../custom_security/reset_password.html | 1 + .../custom_security/send_confirmation.html | 1 + .../templates/custom_security/send_login.html | 1 + 11 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 tests/test_app/templates/custom_security/forgot_password.html create mode 100644 tests/test_app/templates/custom_security/login_user.html create mode 100644 tests/test_app/templates/custom_security/register_user.html create mode 100644 tests/test_app/templates/custom_security/reset_password.html create mode 100644 tests/test_app/templates/custom_security/send_confirmation.html create mode 100644 tests/test_app/templates/custom_security/send_login.html diff --git a/docs/configuration.rst b/docs/configuration.rst index e187a16..1b8f56b 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -96,6 +96,35 @@ URLs and Views =============================== ================================================ +Template Paths +-------------- + +.. tabularcolumns:: |p{6.5cm}|p{8.5cm}| + +======================================== ======================================= +``SECURITY_FORGOT_PASSWORD_TEMPLATE`` Specifies the path to the template for + the forgot password page. Defaults to + ``security/forgot_password.html``. +``SECURITY_LOGIN_USER_TEMPLATE`` Specifies the path to the template for + the user login page. Defaults to + ``security/login_user.html``. +``SECURITY_REGISTER_USER_TEMPLATE`` Specifies the path to the template for + the user registration page. Defaults to + ``security/register_user.html``. +``SECURITY_RESET_PASSWORD_TEMPLATE`` Specifies the path to the template for + the reset password page. Defaults to + ``security/reset_password.html``. +``SECURITY_SEND_CONFIRMATION_TEMPLATE`` Specifies the path to the template for + the resend confirmation instructions + page. Defaults to + ``security/send_confirmation.html``. +``SECURITY_SEND_LOGIN_TEMPLATE`` Specifies the path to the template for + the send login instructions page for + passwordless logins. Defaults to + ``security/send_login.html``. +======================================== ======================================= + + Feature Flags ------------- diff --git a/docs/customizing.rst b/docs/customizing.rst index 8bc5601..1eabf27 100644 --- a/docs/customizing.rst +++ b/docs/customizing.rst @@ -26,9 +26,11 @@ Overriding these templates is simple: 1. Create a folder named ``security`` within your application's templates folder 2. Create a template with the same name for the template you wish to override +You can also specify custom template file paths in the :doc:`configuration `. + Each template is passed a template context object that includes the following, including the objects/values that are passed to the template by the main -Flask application context processory: +Flask application context processor: * ``_form``: A form object for the view * ``security``: The Flask-Security extension object diff --git a/flask_security/core.py b/flask_security/core.py index b30af10..a06c5d0 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -48,6 +48,12 @@ _default_config = { 'POST_CONFIRM_VIEW': None, 'POST_RESET_VIEW': None, 'UNAUTHORIZED_VIEW': None, + 'FORGOT_PASSWORD_TEMPLATE': 'security/forgot_password.html', + 'LOGIN_USER_TEMPLATE': 'security/login_user.html', + 'REGISTER_USER_TEMPLATE': 'security/register_user.html', + 'RESET_PASSWORD_TEMPLATE': 'security/reset_password.html', + 'SEND_CONFIRMATION_TEMPLATE': 'security/send_confirmation.html', + 'SEND_LOGIN_TEMPLATE': 'security/send_login.html', 'CONFIRMABLE': False, 'REGISTERABLE': False, 'RECOVERABLE': False, diff --git a/flask_security/views.py b/flask_security/views.py index 3efd316..f09d8b5 100644 --- a/flask_security/views.py +++ b/flask_security/views.py @@ -23,7 +23,8 @@ from .recoverable import reset_password_token_status, \ send_reset_password_instructions, update_password from .registerable import register_user from .utils import get_url, get_post_login_redirect, do_flash, \ - get_message, login_user, logout_user, url_for_security as url_for + get_message, login_user, logout_user, url_for_security as url_for, \ + config_value # Convenient references @@ -81,7 +82,7 @@ def login(): if request.json: return _render_json(form, True) - return render_template('security/login_user.html', + return render_template(config_value('LOGIN_USER_TEMPLATE'), login_user_form=form, **_ctx('login')) @@ -127,7 +128,7 @@ def register(): if request.json: return _render_json(form) - return render_template('security/register_user.html', + return render_template(config_value('REGISTER_USER_TEMPLATE'), register_user_form=form, **_ctx('register')) @@ -150,7 +151,7 @@ def send_login(): if request.json: return _render_json(form) - return render_template('security/send_login.html', + return render_template(config_value('SEND_LOGIN_TEMPLATE'), send_login_form=form, **_ctx('send_login')) @@ -195,7 +196,7 @@ def send_confirmation(): if request.json: return _render_json(form) - return render_template('security/send_confirmation.html', + return render_template(config_value('SEND_CONFIRMATION_TEMPLATE'), send_confirmation_form=form, **_ctx('send_confirmation')) @@ -243,7 +244,7 @@ def forgot_password(): if request.json: return _render_json(form) - return render_template('security/forgot_password.html', + return render_template(config_value('FORGOT_PASSWORD_TEMPLATE'), forgot_password_form=form, **_ctx('forgot_password')) @@ -272,7 +273,7 @@ def reset_password(token): return redirect(get_url(_security.post_reset_view) or get_url(_security.post_login_view)) - return render_template('security/reset_password.html', + return render_template(config_value('RESET_PASSWORD_TEMPLATE'), reset_password_form=form, reset_password_token=token, **_ctx('reset_password')) diff --git a/tests/configured_tests.py b/tests/configured_tests.py index f1e0d7d..d56854d 100644 --- a/tests/configured_tests.py +++ b/tests/configured_tests.py @@ -107,6 +107,79 @@ class BadConfiguredSecurityTests(SecurityTest): self.assertRaises(RuntimeError, self.authenticate) +class DefaultTemplatePathTests(SecurityTest): + AUTH_CONFIG = { + 'SECURITY_LOGIN_USER_TEMPLATE': 'custom_security/login_user.html', + } + + + def test_login_user_template(self): + r = self._get('/login') + + self.assertIn('CUSTOM LOGIN USER', r.data) + + +class RegisterableTemplatePathTests(SecurityTest): + AUTH_CONFIG = { + 'SECURITY_REGISTERABLE': True, + 'SECURITY_REGISTER_USER_TEMPLATE': 'custom_security/register_user.html' + } + + def test_register_user_template(self): + r = self._get('/register') + + self.assertIn('CUSTOM REGISTER USER', r.data) + + +class RecoverableTemplatePathTests(SecurityTest): + AUTH_CONFIG = { + 'SECURITY_RECOVERABLE': True, + 'SECURITY_FORGOT_PASSWORD_TEMPLATE': 'custom_security/forgot_password.html', + 'SECURITY_RESET_PASSWORD_TEMPLATE': 'custom_security/reset_password.html', + } + + def test_forgot_password_template(self): + r = self._get('/reset') + + self.assertIn('CUSTOM FORGOT PASSWORD', r.data) + + def test_reset_password_template(self): + with capture_reset_password_requests() as requests: + r = self.client.post('/reset', + data=dict(email='joe@lp.com'), + follow_redirects=True) + t = requests[0]['token'] + + r = self._get('/reset/' + t) + + self.assertIn('CUSTOM RESET PASSWORD', r.data) + + +class ConfirmableTemplatePathTests(SecurityTest): + AUTH_CONFIG = { + 'SECURITY_CONFIRMABLE': True, + 'SECURITY_SEND_CONFIRMATION_TEMPLATE': 'custom_security/send_confirmation.html' + } + + def test_send_confirmation_template(self): + r = self._get('/confirm') + + self.assertIn('CUSTOM SEND CONFIRMATION', r.data) + + +class PasswordlessTemplatePathTests(SecurityTest): + AUTH_CONFIG = { + 'SECURITY_PASSWORDLESS': True, + 'SECURITY_SEND_LOGIN_TEMPLATE': 'custom_security/send_login.html' + } + + def test_send_login_template(self): + r = self._get('/login') + + self.assertIn('CUSTOM SEND LOGIN', r.data) + + + class RegisterableTests(SecurityTest): AUTH_CONFIG = { 'SECURITY_REGISTERABLE': True, diff --git a/tests/test_app/templates/custom_security/forgot_password.html b/tests/test_app/templates/custom_security/forgot_password.html new file mode 100644 index 0000000..e268b44 --- /dev/null +++ b/tests/test_app/templates/custom_security/forgot_password.html @@ -0,0 +1 @@ +CUSTOM FORGOT PASSWORD diff --git a/tests/test_app/templates/custom_security/login_user.html b/tests/test_app/templates/custom_security/login_user.html new file mode 100644 index 0000000..abbe2c1 --- /dev/null +++ b/tests/test_app/templates/custom_security/login_user.html @@ -0,0 +1 @@ +CUSTOM LOGIN USER diff --git a/tests/test_app/templates/custom_security/register_user.html b/tests/test_app/templates/custom_security/register_user.html new file mode 100644 index 0000000..927c1f0 --- /dev/null +++ b/tests/test_app/templates/custom_security/register_user.html @@ -0,0 +1 @@ +CUSTOM REGISTER USER diff --git a/tests/test_app/templates/custom_security/reset_password.html b/tests/test_app/templates/custom_security/reset_password.html new file mode 100644 index 0000000..dc8e874 --- /dev/null +++ b/tests/test_app/templates/custom_security/reset_password.html @@ -0,0 +1 @@ +CUSTOM RESET PASSWORD diff --git a/tests/test_app/templates/custom_security/send_confirmation.html b/tests/test_app/templates/custom_security/send_confirmation.html new file mode 100644 index 0000000..a01892d --- /dev/null +++ b/tests/test_app/templates/custom_security/send_confirmation.html @@ -0,0 +1 @@ +CUSTOM SEND CONFIRMATION diff --git a/tests/test_app/templates/custom_security/send_login.html b/tests/test_app/templates/custom_security/send_login.html new file mode 100644 index 0000000..7ef81de --- /dev/null +++ b/tests/test_app/templates/custom_security/send_login.html @@ -0,0 +1 @@ +CUSTOM SEND LOGIN From 4c203a4777109987278e3976cf051fd533eb7823 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Tue, 29 Jan 2013 22:29:31 -0500 Subject: [PATCH 5/7] Removed extra line. --- tests/configured_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/configured_tests.py b/tests/configured_tests.py index d56854d..41c34d4 100644 --- a/tests/configured_tests.py +++ b/tests/configured_tests.py @@ -179,7 +179,6 @@ class PasswordlessTemplatePathTests(SecurityTest): self.assertIn('CUSTOM SEND LOGIN', r.data) - class RegisterableTests(SecurityTest): AUTH_CONFIG = { 'SECURITY_REGISTERABLE': True, From ad6227006ebb4a8b5101cd3a973275e778f96400 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Fri, 1 Feb 2013 19:41:42 -0500 Subject: [PATCH 6/7] Fixed lack of newline in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bc669f6..e17f9ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ Flask-Mail>=0.7.3 Flask-Principal>=0.3.3 Flask-WTF>=0.8 itsdangerous>=0.17 -passlib>=1.6.1 \ No newline at end of file +passlib>=1.6.1 From dbc2dcc625c94496acd667bcb72211866d742d58 Mon Sep 17 00:00:00 2001 From: Chris Haines Date: Fri, 1 Feb 2013 19:43:13 -0500 Subject: [PATCH 7/7] Removed flask-script as a dependency in setup.py --- setup.py | 1 - tests/configured_tests.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 580afa2..05e8986 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,6 @@ setup( 'Flask-Login>=0.1.3', 'Flask-Mail>=0.7.3', 'Flask-Principal>=0.3.3', - 'Flask-Script==0.5.3', 'Flask-WTF>=0.8', 'itsdangerous>=0.17', 'passlib>=1.6.1', diff --git a/tests/configured_tests.py b/tests/configured_tests.py index 0ac4149..ed84831 100644 --- a/tests/configured_tests.py +++ b/tests/configured_tests.py @@ -148,7 +148,7 @@ class RecoverableTemplatePathTests(SecurityTest): r = self._post('/reset', data=dict(email='joe@lp.com'), follow_redirects=True) - + t = requests[0]['token'] r = self._get('/reset/' + t)