From ecded8af933ac466d0718eadee61ce6e80d1748c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Tue, 27 Jun 2023 01:07:26 +0200 Subject: [PATCH] Added password meter based on zxcvbn Maybe we will use a different package later, as this one is very big... --- .../password_strength_estimate_controller.js | 89 +++++++++++++++++++ package.json | 3 +- src/Controller/UserSettingsController.php | 1 + src/Form/PasswordTypeExtension.php | 54 +++++++++++ src/Form/UserAdminForm.php | 1 + .../form/extended_bootstrap_layout.html.twig | 18 ++++ yarn.lock | 5 ++ 7 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 assets/controllers/elements/password_strength_estimate_controller.js create mode 100644 src/Form/PasswordTypeExtension.php diff --git a/assets/controllers/elements/password_strength_estimate_controller.js b/assets/controllers/elements/password_strength_estimate_controller.js new file mode 100644 index 00000000..92a4dc20 --- /dev/null +++ b/assets/controllers/elements/password_strength_estimate_controller.js @@ -0,0 +1,89 @@ +/* + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +import {Controller} from "@hotwired/stimulus"; +import zxcvbn from "zxcvbn"; + +export default class extends Controller { + + _passwordInput; + + static targets = ["badge", "suggestion", "warning"] + + connect() { + //Find the password input field + this._passwordInput = this.element.querySelector('input[type="password"]'); + + //Add event listener to the password input field + this._passwordInput.addEventListener('input', this._onPasswordInput.bind(this)); + + } + + _onPasswordInput() { + //Retrieve the password + const password = this._passwordInput.value; + + //Estimate the password strength + const result = zxcvbn(password); + + //Update the badge + this.badgeTarget.parentElement.classList.remove("d-none"); + this._setBadgeToLevel(result.score); + + this.warningTarget.innerHTML = result.feedback.warning; + } + + _setBadgeToLevel(level) { + let text, classes; + + switch (level) { + case 0: + text = "Very weak"; + classes = "bg-danger badge-danger"; + break; + case 1: + text = "Weak"; + classes = "bg-warning badge-warning"; + break; + case 2: + text = "Medium"; + classes = "bg-info badge-info"; + break; + case 3: + text = "Strong"; + classes = "bg-primary badge-primary"; + break; + case 4: + text = "Very strong"; + classes = "bg-success badge-success"; + break; + default: + text = "Unknown"; + classes = "bg-secondary badge-secondary"; + } + + this.badgeTarget.innerHTML = text; + //Remove all classes + this.badgeTarget.className = ''; + //Re-add the classes + this.badgeTarget.classList.add("badge"); + this.badgeTarget.classList.add(...classes.split(" ")); + } +} \ No newline at end of file diff --git a/package.json b/package.json index 3fb2eada..e0511d14 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "stimulus-use": "^0.52.0", "tom-select": "^2.1.0", "ts-loader": "^9.2.6", - "typescript": "^4.0.2" + "typescript": "^4.0.2", + "zxcvbn": "^4.4.2" } } diff --git a/src/Controller/UserSettingsController.php b/src/Controller/UserSettingsController.php index ce407a6b..704bacb7 100644 --- a/src/Controller/UserSettingsController.php +++ b/src/Controller/UserSettingsController.php @@ -285,6 +285,7 @@ class UserSettingsController extends AbstractController 'type' => PasswordType::class, 'first_options' => [ 'label' => 'user.settings.pw_new.label', + 'password_estimator' => true, ], 'second_options' => [ 'label' => 'user.settings.pw_confirm.label', diff --git a/src/Form/PasswordTypeExtension.php b/src/Form/PasswordTypeExtension.php new file mode 100644 index 00000000..f97eeb8e --- /dev/null +++ b/src/Form/PasswordTypeExtension.php @@ -0,0 +1,54 @@ +. + */ + +namespace App\Form; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * Purpose of this class is to add the setting 'password_estimator' to the PasswordType. + */ +class PasswordTypeExtension extends AbstractTypeExtension +{ + + public static function getExtendedTypes(): iterable + { + return [PasswordType::class]; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'password_estimator' => false, + ]); + + $resolver->setAllowedTypes('password_estimator', 'bool'); + } + + public function finishView(FormView $view, FormInterface $form, array $options) + { + $view->vars['password_estimator'] = $options['password_estimator']; + } + +} \ No newline at end of file diff --git a/src/Form/UserAdminForm.php b/src/Form/UserAdminForm.php index f8fcc7c6..d1e5924e 100644 --- a/src/Form/UserAdminForm.php +++ b/src/Form/UserAdminForm.php @@ -167,6 +167,7 @@ class UserAdminForm extends AbstractType 'type' => PasswordType::class, 'first_options' => [ 'label' => 'user.settings.pw_new.label', + 'password_estimator' => true, ], 'second_options' => [ 'label' => 'user.settings.pw_confirm.label', diff --git a/templates/form/extended_bootstrap_layout.html.twig b/templates/form/extended_bootstrap_layout.html.twig index 2a599672..e1ea2340 100644 --- a/templates/form/extended_bootstrap_layout.html.twig +++ b/templates/form/extended_bootstrap_layout.html.twig @@ -122,4 +122,22 @@ {% block part_select_widget %} {{ form_widget(form.autocomplete) }} +{% endblock %} + +{% block password_widget %} + {# If password_estimator setting is not set render it like normal #} + {% if password_estimator %} +
+
+ + + + {{- parent() -}} +
+ + +
+ {% else %} + {{- parent() -}} + {% endif %} {% endblock %} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 79d51940..49bfc66e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6926,3 +6926,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zxcvbn@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" + integrity sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==