feat: WIP! password strength indicator
This commit is contained in:
parent
b84b2bd615
commit
e3c1aab6e2
137
Cargo.lock
generated
137
Cargo.lock
generated
@ -707,6 +707,21 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -776,6 +791,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"static-files",
|
||||
"zxcvbn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1010,6 +1026,41 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
@ -1019,6 +1070,37 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.18"
|
||||
@ -1160,6 +1242,17 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
@ -1543,6 +1636,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@ -1602,6 +1701,15 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@ -1641,6 +1749,12 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.11.7"
|
||||
@ -2641,6 +2755,12 @@ dependencies = [
|
||||
"unicode-properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@ -3259,3 +3379,20 @@ dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zxcvbn"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad76e35b00ad53688d6b90c431cabe3cbf51f7a4a154739e04b63004ab1c736c"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"fancy-regex",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
@ -28,6 +28,7 @@ lettre = "0.11.7"
|
||||
quick-xml = { version = "0.31.0", features = ["serde", "serialize"] }
|
||||
actix-web-static-files = "4.0"
|
||||
static-files = "0.2.1"
|
||||
zxcvbn = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.7.4"
|
||||
|
@ -7,6 +7,10 @@ use lettre::{
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
use zxcvbn::{
|
||||
feedback::{Suggestion, Warning},
|
||||
zxcvbn, Score,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
auth,
|
||||
@ -19,6 +23,7 @@ struct ResetPasswordForm {
|
||||
token: Option<String>,
|
||||
password: Option<String>,
|
||||
passwordretyped: Option<String>,
|
||||
dry: Option<bool>,
|
||||
}
|
||||
|
||||
#[actix_web::post("/reset-password")]
|
||||
@ -91,6 +96,63 @@ Viele Grüße"##, user.name, reset_url))
|
||||
return HttpResponse::BadRequest().body("Token existiert nicht bzw. ist abgelaufen!");
|
||||
}
|
||||
|
||||
let user = User::read_by_id(pool.get_ref(), token.as_ref().unwrap().id)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut split_names: Vec<&str> = user.name.as_str().split_whitespace().collect();
|
||||
let mut user_inputs = vec![user.email.as_str()];
|
||||
user_inputs.append(&mut split_names);
|
||||
|
||||
let entropy = zxcvbn(form.password.as_ref().unwrap(), &user_inputs);
|
||||
|
||||
if entropy.score() < Score::Three {
|
||||
let feedback = entropy.feedback().unwrap();
|
||||
|
||||
let warning = match feedback.warning() {
|
||||
Some(Warning::ThisIsATop10Password) => {
|
||||
"Das Passwort ist eins der 10 meist genutzten."
|
||||
}
|
||||
Some(Warning::ThisIsATop100Password) => {
|
||||
"Das Passwort ist eins der 100 meist genutzten."
|
||||
}
|
||||
Some(Warning::ThisIsACommonPassword) => "Das ist ein zu übliches Password.",
|
||||
Some(Warning::DatesAreOftenEasyToGuess) => {
|
||||
"Datumsangaben lassen sich meist leicht erraten."
|
||||
}
|
||||
Some(Warning::RecentYearsAreEasyToGuess) => {
|
||||
"Vergangene Jahreszahlen lassen sich leicht erraten."
|
||||
}
|
||||
Some(Warning::AWordByItselfIsEasyToGuess) => "abc",
|
||||
Some(Warning::RepeatsLikeAaaAreEasyToGuess) => "abc",
|
||||
Some(Warning::SequencesLikeAbcAreEasyToGuess) => "abc",
|
||||
Some(Warning::StraightRowsOfKeysAreEasyToGuess) => "abc",
|
||||
Some(Warning::ShortKeyboardPatternsAreEasyToGuess) => "abc",
|
||||
Some(Warning::ThisIsSimilarToACommonlyUsedPassword) => "abc",
|
||||
Some(Warning::CommonNamesAndSurnamesAreEasyToGuess) => "abc",
|
||||
Some(Warning::NamesAndSurnamesByThemselvesAreEasyToGuess) => "abc",
|
||||
Some(Warning::RepeatsLikeAbcAbcAreOnlySlightlyHarderToGuess) => "abc",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let suggestion = feedback
|
||||
.suggestions()
|
||||
.iter()
|
||||
.map(|s| match s {
|
||||
Suggestion::AvoidSequences => "abc",
|
||||
Suggestion::AvoidRecentYears => "abc",
|
||||
_ => "abc",
|
||||
})
|
||||
.collect::<Vec<&str>>()
|
||||
.join("</br>");
|
||||
|
||||
return HttpResponse::BadRequest().body(format!("Passwort zu schwach.</br>Warnung: {warning}</br>Vorschlag: {suggestion}"));
|
||||
}
|
||||
|
||||
if form.dry.is_some_and(|b| b) {
|
||||
return HttpResponse::NoContent().finish();
|
||||
}
|
||||
|
||||
|
||||
if form.password.as_ref().unwrap() != form.passwordretyped.as_ref().unwrap() {
|
||||
return HttpResponse::BadRequest().body("Passwörter stimmen nicht überein!");
|
||||
}
|
||||
|
@ -4,20 +4,22 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">Brass - Passwort zurücksetzen</h1>
|
||||
<form class="box" hx-post="/reset-password" hx-target-400="#error-message" hx-on:change="document.getElementById('error-message').innerHTML = ''">
|
||||
<form class="box" hx-post="/reset-password" hx-params="not dry" hx-target-400="#error-message" hx-on:change="document.getElementById('error-message').innerHTML = ''">
|
||||
<input type="hidden" name="token" value="{{ token }}"/>
|
||||
|
||||
<input type="hidden" name="dry" value="true"/>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="password">neues Passwort:</label>
|
||||
<div class="control">
|
||||
<input class="input" placeholder="**********" name="password" type="password" required>
|
||||
<input class="input" hx-post="/reset-password?dry=true" hx-params="*" hx-trigger="keyup changed delay:1s" hx-target-400="#error-message" placeholder="**********" name="password" type="password" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="passwordretyped">neues Passwort wiederholen:</label>
|
||||
<div class="control">
|
||||
<input class="input" placeholder="**********" name="passwordretyped" type="password" required>
|
||||
<input class="input" placeholder="**********" name="passwordretyped" type="password" max=256 required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user