added client side check for trivial password policies (#32846)
* added client side check for trivial password policies fixes: #32845 Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * changed to make use of template Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * nicer active policy structure Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> --------- Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
0f97e4cb39
commit
22f9d2077e
3 changed files with 99 additions and 1 deletions
|
@ -14,6 +14,7 @@
|
|||
|
||||
<#nested>
|
||||
|
||||
<div id="input-error-client-${name}"></div>
|
||||
<#if error?has_content>
|
||||
<div class="${properties.kcFormHelperTextClass}" aria-live="polite">
|
||||
<div class="${properties.kcInputHelperTextClass}">
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
</#if>
|
||||
<#elseif section = "form">
|
||||
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post" novalidate="novalidate">
|
||||
|
||||
<@userProfileCommons.userProfileFormFields; callback, attribute>
|
||||
<#if callback = "afterField">
|
||||
<#-- render password fields just under the username or email (if used as username) -->
|
||||
|
@ -46,5 +45,50 @@
|
|||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<template id="errorTemplate">
|
||||
<div class="${properties.kcFormHelperTextClass}" aria-live="polite">
|
||||
<div class="${properties.kcInputHelperTextClass}">
|
||||
<div class="${properties.kcInputHelperTextItemClass} ${properties.kcError}">
|
||||
<ul class="${properties.kcInputErrorMessageClass}">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template id="errorItemTemplate">
|
||||
<li></li>
|
||||
</template>
|
||||
|
||||
<script type="module">
|
||||
import { validatePassword } from "${url.resourcesPath}/js/password-policy.js";
|
||||
|
||||
const template = document.querySelector("#errorTemplate").content.cloneNode(true);
|
||||
|
||||
const activePolicies = [
|
||||
{ name: "length", policy: { value: ${passwordPolicies.length!-1}, error: "${msg('invalidPasswordMinLengthMessage')}"} },
|
||||
{ name: "maxLength", policy: { value: ${passwordPolicies.maxLength!-1}, error: "${msg('invalidPasswordMaxLengthMessage')}"} },
|
||||
{ name: "lowerCase", policy: { value: ${passwordPolicies.lowerCase!-1}, error: "${msg('invalidPasswordMinLowerCaseCharsMessage')}"} },
|
||||
{ name: "upperCase", policy: { value: ${passwordPolicies.upperCase!-1}, error: "${msg('invalidPasswordMinUpperCaseCharsMessage')}"} },
|
||||
{ name: "digits", policy: { value: ${passwordPolicies.digits!-1}, error: "${msg('invalidPasswordMinDigitsMessage')}"} },
|
||||
{ name: "specialChars", policy: { value: ${passwordPolicies.specialChars!-1}, error: "${msg('invalidPasswordMinSpecialCharsMessage')}"} }
|
||||
].filter(p => p.policy.value !== -1);
|
||||
|
||||
document.getElementById("password").addEventListener("change", (event) => {
|
||||
const serverErrors = document.getElementById("input-error-password");
|
||||
if (serverErrors) {
|
||||
serverErrors.remove();
|
||||
}
|
||||
const errors = validatePassword(event.target.value, activePolicies);
|
||||
const errorList = template.querySelector("ul");
|
||||
const htmlErrors = errors.forEach((e) => {
|
||||
const row = document.querySelector("#errorItemTemplate").content.cloneNode(true);
|
||||
const li = row.querySelector("li");
|
||||
li.textContent = e;
|
||||
errorList.appendChild(li);
|
||||
});
|
||||
document.getElementById("input-error-client-password").appendChild(template);
|
||||
});
|
||||
</script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
const policies = {
|
||||
length: (policy, value) => {
|
||||
if (value.length < policy.value) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
maxLength: (policy, value) => {
|
||||
if (value.length > policy.value) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
upperCase: (policy, value) => {
|
||||
if (
|
||||
value.split("").filter((char) => char !== char.toUpperCase()).length >
|
||||
policy.value
|
||||
) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
lowerCase: (policy, value) => {
|
||||
if (
|
||||
value.split("").filter((char) => char !== char.toLowerCase()).length >
|
||||
policy.value
|
||||
) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
digits: (policy, value) => {
|
||||
const digits = value.split("").filter((char) => char.match(/\d/));
|
||||
if (digits.length < policy.value) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
specialChars: (policy, value) => {
|
||||
let specialChars = value.split("").filter((char) => char.match(/\W/));
|
||||
if (specialChars.length < policy.value) {
|
||||
return templateError(policy);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const templateError = (policy) => policy.error.replace("{0}", policy.value);
|
||||
|
||||
export function validatePassword(password, activePolicies) {
|
||||
const errors = [];
|
||||
for (const p of activePolicies) {
|
||||
const validationError = policies[p.name](p.policy, password);
|
||||
if (validationError) {
|
||||
errors.push(validationError);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
Loading…
Reference in a new issue