diff --git a/apps/fz_http/assets/js/hooks.js b/apps/fz_http/assets/js/hooks.js index 578998b8c..d4d3a0018 100644 --- a/apps/fz_http/assets/js/hooks.js +++ b/apps/fz_http/assets/js/hooks.js @@ -1,5 +1,5 @@ import hljs from "highlight.js" -import {FormatTimestamp} from "./util.js" +import {FormatTimestamp,PasswordStrength} from "./util.js" import {renderQrCode} from "./qrcode.js" const highlightCode = function () { @@ -11,6 +11,46 @@ const formatTimestamp = function () { this.el.innerHTML = FormatTimestamp(t) } +const passwordStrength = function () { + const field = this.el + const fieldClasses = "password input " + const progress = document.getElementById(field.dataset.target) + const reset = function () { + field.className = fieldClasses + progress.className = "is-hidden" + progress.setAttribute("value", "0") + progress.innerHTML = "0%" + } + field.addEventListener("input", () => { + if (field.value === "") return reset() + const score = PasswordStrength(field.value) + switch (score) { + case 0: + case 1: + field.className = fieldClasses + "is-danger" + progress.className = "progress is-small is-danger" + progress.setAttribute("value", "33") + progress.innerHTML = "33%" + break + case 2: + case 3: + field.className = fieldClasses + "is-warning" + progress.className = "progress is-small is-warning" + progress.setAttribute("value", "67") + progress.innerHTML = "67%" + break + case 4: + field.className = fieldClasses + "is-success" + progress.className = "progress is-small is-success" + progress.setAttribute("value", "100") + progress.innerHTML = "100%" + break + default: + reset() + } + }) +} + const clipboardCopy = function () { let button = this.el let data = button.dataset.clipboard @@ -37,5 +77,9 @@ Hooks.FormatTimestamp = { mounted: formatTimestamp, updated: formatTimestamp } +Hooks.PasswordStrength = { + mounted: passwordStrength, + updated: passwordStrength +} export default Hooks diff --git a/apps/fz_http/assets/js/util.js b/apps/fz_http/assets/js/util.js index c9263a031..60983aa90 100644 --- a/apps/fz_http/assets/js/util.js +++ b/apps/fz_http/assets/js/util.js @@ -1,4 +1,5 @@ import moment from "moment" +import zxcvbn from "zxcvbn" const FormatTimestamp = function (timestamp) { if (timestamp) { @@ -8,4 +9,9 @@ const FormatTimestamp = function (timestamp) { } } -export { FormatTimestamp } +const PasswordStrength = function (password) { + const result = zxcvbn(password) + return result.score +} + +export { PasswordStrength, FormatTimestamp } diff --git a/apps/fz_http/assets/package-lock.json b/apps/fz_http/assets/package-lock.json index 87b4ba15d..03c984d41 100644 --- a/apps/fz_http/assets/package-lock.json +++ b/apps/fz_http/assets/package-lock.json @@ -43,7 +43,8 @@ "source-map": "^0.7.3", "url-loader": "^4.1.1", "webpack": "5.36.0", - "webpack-cli": "^4.9.2" + "webpack-cli": "^4.9.2", + "zxcvbn": "^4.4.2" }, "engines": { "node": ">= 14.0.0" @@ -8061,6 +8062,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=", + "dev": true } }, "dependencies": { @@ -13904,6 +13911,12 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=", + "dev": true } } } diff --git a/apps/fz_http/assets/package.json b/apps/fz_http/assets/package.json index 8454d69d6..56cd5cf5e 100644 --- a/apps/fz_http/assets/package.json +++ b/apps/fz_http/assets/package.json @@ -48,6 +48,7 @@ "source-map": "^0.7.3", "url-loader": "^4.1.1", "webpack": "5.36.0", - "webpack-cli": "^4.9.2" + "webpack-cli": "^4.9.2", + "zxcvbn": "^4.4.2" } } diff --git a/apps/fz_http/lib/fz_http/users/user.ex b/apps/fz_http/lib/fz_http/users/user.ex index 5e74ab5de..c930cc6c1 100644 --- a/apps/fz_http/lib/fz_http/users/user.ex +++ b/apps/fz_http/lib/fz_http/users/user.ex @@ -3,7 +3,7 @@ defmodule FzHttp.Users.User do Represents a User. """ - @min_password_length 8 + @min_password_length 12 @max_password_length 64 use Ecto.Schema @@ -173,4 +173,6 @@ defmodule FzHttp.Users.User do {:error, error_msg} -> changeset |> add_error(:current_password, error_msg) end end + + defp verify_current_password(changeset, _user), do: changeset end diff --git a/apps/fz_http/lib/fz_http_web/error_helpers.ex b/apps/fz_http/lib/fz_http_web/error_helpers.ex index 8cd1db5f6..b1bea5e8b 100644 --- a/apps/fz_http/lib/fz_http_web/error_helpers.ex +++ b/apps/fz_http/lib/fz_http_web/error_helpers.ex @@ -29,6 +29,19 @@ defmodule FzHttpWeb.ErrorHelpers do end) end + @doc """ + Adds "is-danger" to input elements that have errors + """ + def input_error_class(form, field) do + case Keyword.get_values(form.errors, field) do + [] -> + "" + + _ -> + "is-danger" + end + end + @doc """ Translates an error message using gettext. """ diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex index 3168a8bad..64e031ad2 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/form_component.html.heex @@ -3,7 +3,7 @@
<%= error_tag f, :name %> @@ -30,7 +30,8 @@
<%= error_tag f, :allowed_ips %> @@ -57,7 +58,8 @@
<%= error_tag f, :dns %> @@ -85,7 +87,8 @@ <%= label f, :endpoint, "Server Endpoint", class: "label" %>
The IP of the server this device should connect to.
<%= error_tag f, :endpoint %> @@ -113,7 +116,7 @@ <%= label f, :mtu, "Interface MTU", class: "label" %>
The WireGuard interface MTU for this Device.
<%= error_tag f, :mtu %> @@ -146,7 +149,8 @@ unless you're experiencing NAT or firewall traversal problems.
<%= error_tag f, :persistent_keepalive %> @@ -157,7 +161,7 @@ <%= label f, :ipv4, "IPv4 Address", class: "label" %>
<%= error_tag f, :ipv4 %> @@ -168,7 +172,7 @@ <%= label f, :ipv6, "IPv6 Address", class: "label" %>
<%= error_tag f, :ipv6 %> diff --git a/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.html.heex b/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.html.heex index 44e20768a..d3bb5ee71 100644 --- a/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.html.heex +++ b/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.html.heex @@ -12,7 +12,7 @@
<%= error_tag f, :email %>
- <%= error_tag f, :password %> -
-- <%= error_tag f, :password_confirmation %> -
-<%= error_tag f, :email %>
- <%= error_tag f, :password %> -
-- <%= error_tag f, :password_confirmation %> -
-<%= error_tag f, :email %> @@ -23,7 +22,7 @@
<%= error_tag f, :password %> diff --git a/apps/fz_http/lib/fz_http_web/templates/shared/password_field.html.heex b/apps/fz_http/lib/fz_http_web/templates/shared/password_field.html.heex new file mode 100644 index 000000000..43df49c32 --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/templates/shared/password_field.html.heex @@ -0,0 +1,21 @@ +
+ <%= error_tag @context, @field %> +
+ + +