From fc5fea2c2fe5588bc55b92dacd97da6f5ad2f265 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Fri, 14 Jun 2013 16:55:29 +0200 Subject: [PATCH] Fix #1131 Add password strength validation --- component.json | 3 +- lib/Alchemy/Phrasea/Controller/Root/Login.php | 7 +- .../web/account/change-password.html.twig | 1 + .../common/password_strength_widget.html.twig | 15 ++++ templates/web/login/common/macros.html.twig | 3 + .../web/login/register-classic.html.twig | 1 + templates/web/login/renew-password.html.twig | 1 + .../apps/login/home/recoverPassword.js | 2 +- www/scripts/apps/login/home/register.js | 2 +- www/scripts/apps/login/home/renewPassword.js | 2 +- www/scripts/apps/login/home/views/form.js | 4 +- .../login/home/views/forms/passwordSetter.js | 80 +++++++++++++++++++ www/skins/login/less/skin.less | 44 ++++++++++ 13 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 templates/web/common/password_strength_widget.html.twig create mode 100644 www/scripts/apps/login/home/views/forms/passwordSetter.js diff --git a/component.json b/component.json index 827bda4238..58b5ae64f3 100644 --- a/component.json +++ b/component.json @@ -23,6 +23,7 @@ "sinon": "~1.7", "sinon-chai": "~2.4", "js-fixtures": "https://github.com/badunk/js-fixtures/archive/master.zip", - "bootstrap-multiselect": "https://github.com/davidstutz/bootstrap-multiselect.git" + "bootstrap-multiselect": "https://github.com/davidstutz/bootstrap-multiselect.git", + "zxcvbn" : "https://github.com/lowe/zxcvbn.git" } } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index 58b47aaecd..21d6e17e4c 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -191,7 +191,12 @@ class Login implements ControllerProviderInterface 'no_collection_selected' => _('No collection selected'), 'one_collection_selected' => _('%d collection selected'), 'collections_selected' => _('%d collections selected'), - 'all_collections' => _('Select all collections') + 'all_collections' => _('Select all collections'), + // password strength + 'weak' => _('Weak'), + 'ordinary' => _('Ordinary'), + 'good' => _('Good'), + 'great' => _('Great'), )); $response->setExpires(new \DateTime('+1 day')); diff --git a/templates/web/account/change-password.html.twig b/templates/web/account/change-password.html.twig index a179b439c4..9eab4bcd13 100644 --- a/templates/web/account/change-password.html.twig +++ b/templates/web/account/change-password.html.twig @@ -54,5 +54,6 @@ {% block scripts %} {{ parent() }} + {% endblock %} diff --git a/templates/web/common/password_strength_widget.html.twig b/templates/web/common/password_strength_widget.html.twig new file mode 100644 index 0000000000..bc32bc4631 --- /dev/null +++ b/templates/web/common/password_strength_widget.html.twig @@ -0,0 +1,15 @@ + + + + + + +
{% trans %}Security{% endtrans %} +
+
+
+
+
+
+
+
diff --git a/templates/web/login/common/macros.html.twig b/templates/web/login/common/macros.html.twig index 9ba280c3e9..02df8b0e0e 100644 --- a/templates/web/login/common/macros.html.twig +++ b/templates/web/login/common/macros.html.twig @@ -28,6 +28,9 @@
{{ _self.fieldInput(passField, form_name, icon_name, custom_attributes) }} + {% if loop.first %} + {% include 'common/password_strength_widget.html.twig' %} + {% endif %}
{% endfor %} diff --git a/templates/web/login/register-classic.html.twig b/templates/web/login/register-classic.html.twig index 2c79282a5a..7f59986fb3 100644 --- a/templates/web/login/register-classic.html.twig +++ b/templates/web/login/register-classic.html.twig @@ -101,5 +101,6 @@ {% block scripts %} {{ parent() }} + {% endblock %} diff --git a/templates/web/login/renew-password.html.twig b/templates/web/login/renew-password.html.twig index 2d8b3a5804..d2b4906943 100644 --- a/templates/web/login/renew-password.html.twig +++ b/templates/web/login/renew-password.html.twig @@ -50,5 +50,6 @@ {% block scripts %} {{ parent() }} + {% endblock %} diff --git a/www/scripts/apps/login/home/recoverPassword.js b/www/scripts/apps/login/home/recoverPassword.js index 85c00ac991..f2e0d87301 100644 --- a/www/scripts/apps/login/home/recoverPassword.js +++ b/www/scripts/apps/login/home/recoverPassword.js @@ -11,7 +11,7 @@ require([ "jquery", "i18n", "apps/login/home/common", - "apps/login/home/views/form" + "apps/login/home/views/forms/passwordSetter" ], function($, i18n, Common, RenewPassword) { i18n.init({ resGetPath: Common.languagePath, diff --git a/www/scripts/apps/login/home/register.js b/www/scripts/apps/login/home/register.js index 36ae325925..874b8905af 100644 --- a/www/scripts/apps/login/home/register.js +++ b/www/scripts/apps/login/home/register.js @@ -12,7 +12,7 @@ require([ "jquery", "i18n", "apps/login/home/common", - "apps/login/home/views/form" + "apps/login/home/views/forms/passwordSetter" ], function($, i18n, Common, RegisterForm) { var fieldsConfiguration = []; diff --git a/www/scripts/apps/login/home/renewPassword.js b/www/scripts/apps/login/home/renewPassword.js index 06c4844ccf..71c3eb2f0c 100644 --- a/www/scripts/apps/login/home/renewPassword.js +++ b/www/scripts/apps/login/home/renewPassword.js @@ -11,7 +11,7 @@ require([ "jquery", "i18n", "apps/login/home/common", - "apps/login/home/views/form" + "apps/login/home/views/forms/passwordSetter" ], function($, i18n, Common, RenewPassword) { i18n.init({ resGetPath: Common.languagePath, diff --git a/www/scripts/apps/login/home/views/form.js b/www/scripts/apps/login/home/views/form.js index d30e2d5a77..bc8904acec 100644 --- a/www/scripts/apps/login/home/views/form.js +++ b/www/scripts/apps/login/home/views/form.js @@ -15,7 +15,7 @@ define([ "common/validator", "apps/login/home/views/input" ], function($, _, Backbone, bootstrap, Validator, InputView) { - var RegisterForm = Backbone.View.extend({ + var Form = Backbone.View.extend({ events: { "submit": "_onSubmit" }, @@ -82,5 +82,5 @@ define([ } }); - return RegisterForm; + return Form; }); diff --git a/www/scripts/apps/login/home/views/forms/passwordSetter.js b/www/scripts/apps/login/home/views/forms/passwordSetter.js new file mode 100644 index 0000000000..05d1c1042f --- /dev/null +++ b/www/scripts/apps/login/home/views/forms/passwordSetter.js @@ -0,0 +1,80 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +define([ + "jquery", + "underscore", + "i18n", + "backbone", + "bootstrap", + "apps/login/home/views/form" +], function($, _, i18n, Backbone, bootstrap, FormView) { + var PasswordSetterForm = FormView.extend({ + events: function(){ + return _.extend({},FormView.prototype.events,{ + 'keyup input[type=password]' : 'onPasswordKeyup' + }); + }, + onPasswordKeyup : function(event) { + var input = $(event.target); + var password = input.val(); + var inputView = this.inputViews[input.attr("name")]; + var bg = $(".password_strength_bg", inputView.$el); + var label = $(".password_strength_label", inputView.$el); + var desc = $(".password_strength_desc", inputView.$el); + var css = { + "width": "0%", + "background-color": "rgb(39, 39, 30)" + }; + var result = ""; + + if (password.length > 0 ) { + var passMeter = zxcvbn(input.val()); + + switch (passMeter.score) { + case 0: + case 1: + css = { + "width": "25%", + "background-color": "rgb(200, 24, 24)" + }; + result = i18n.t("weak"); + break; + case 2: + css = { + "width": "50%", + "background-color": "rgb(255, 172, 29)" + }; + result = i18n.t("ordinary"); + break; + case 3: + css = { + "width": "75%", + "background-color": "rgb(166, 192, 96)" + }; + result = i18n.t("good"); + break; + case 4: + css = { + "width": "100%", + "background-color": "rgb(39, 179, 15)" + }; + result = i18n.t("great"); + break; + } + } + + bg.css(css); + label.css({"color": css["background-color"]}); + desc.css({"color": css["background-color"]}).html(result); + } + }); + + return PasswordSetterForm; +}); diff --git a/www/skins/login/less/skin.less b/www/skins/login/less/skin.less index 7df32e498c..0beb044be1 100644 --- a/www/skins/login/less/skin.less +++ b/www/skins/login/less/skin.less @@ -753,6 +753,50 @@ form[name=registerForm] .multiselect-group { font-size: @fontSizeLarge; } +.password_strength_widget, .password_strength_widget tr, .password_strength_widget td { + border: none !important; +} + +.password_strength_label, .password_strength_desc { + font-size: @fontSizeMini; + color: lighten(@backgroundSideBar, 5%) ; +} + +.password_strength_desc { + text-align: right; +} + +.password_strength_container { + position: relative; + width: 100%; + margin: 5px auto 0 auto; + height: 10px; +} + +.password_strength_bg { + height: 4px; + background-color: lighten(@backgroundSideBar, 5%); + width: 100%; + position: absolute; + left: 0; +} + +.password_strength { + height: 4px; + background-color: #c81818; + width: 0%; + position: absolute; + left: 0; +} + +.password_strength_separator { + height: 4px; + width: 2px; + background-color: @backgroundSideBar; + position: absolute; + left: 0; +} + /** IE Fixes */ .lt-ie8 authentication-sidebar-language .caret {