Skip to content

Commit

Permalink
revisit password expiration policy templates customizations (#30)
Browse files Browse the repository at this point in the history
Reading the Java class mentioned in issue #30, it seems that
only 2 properties were modified in the original messages.properties from
the classpath (what about the other languages ?).

This commit should provide the same behaviour as the original PR. I had
to reintroduce the pwdupdateform.html fragment from upstream, and
customize the (hardcoded ?) URLs to get into the console webservices
meant for password management. FTR, the original template is here:
https://github.com/apereo/cas/blob/6.6.x/support/cas-server-support-thymeleaf/src/main/resources/templates/fragments/pwdupdateform.html

BTW, it looks like CAS provides such password management from the user
on its own code, via an optional module:
https://apereo.github.io/cas/7.0.x/password_management/Password-Management-Reset.html
but since we already have the feature provided by the console, it makes
probably more sense to make use of them instead.

Note: I am not sure why the `password.expiration.warning` does not
seem to be used in any templates in the CAS6.6 codebase.

Note2: we might want to be able to customize the console's endpoint url
from the datadir, but since they were hardcoded in the java class, at
least we are isofunctional here.

tests: runtime, using
https://github.com/georchestra/sample-docker-composition/tree/main/cas/password-expiration
- tested with english & french locales.
  • Loading branch information
pmauduit committed Sep 21, 2024
1 parent dac6426 commit 4b18b6c
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ screen.surrogates.button.cancel=Cancel
screen.surrogates.account.selection.error=You are not authorized to impersonate the indicated user at this time.

# Password policy
password.expiration.warning=Your password expires in {0} day(s). Please <a href="https://pm.example.edu">change your password</a> now.
password.expiration.warning=Your password expires in {0} day(s). Please <a href="/console/account/changePassword">change your password</a> now.
password.expiration.loginsRemaining=You have {0} login(s) remaining before you <strong>MUST</strong> change your password.
screen.accountdisabled.heading=This account has been disabled.
screen.accountdisabled.message=Please contact the system administrator to regain access.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ screen.surrogates.button.cancel=Abbrechen


# Password policy
password.expiration.warning=Ihr Kennwort läuft in {0} Tagen ab. Bitte <a href\="{1}">ändern Sie Ihr Kennwort</a>.
password.expiration.warning=Ihr Kennwort läuft in {0} Tagen ab. Bitte <a href="/console/account/changePassword">ändern Sie Ihr Kennwort</a>.
password.expiration.loginsRemaining=Sie haben {0} Anmeldungen übrig, bevor Sie Ihr Kennwort ändern <strong>müssen</strong>.
screen.accountdisabled.heading=Dieses Konto wurde deaktiviert.
screen.accountdisabled.message=Bitte kontaktieren Sie Ihren System Administrator um wieder Zugriff zu erhalten.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ screen.service.empty.error.message=El registro de servicios del CAS está vacío
Las aplicaciones que quieren autenticar con CAS deben ser explícitamente definidas en el registro de servicios.

# Password policy
password.expiration.warning=Su contraseña caduca en {0} días. Por favor <a href="{1}">cambie su contraseña</a> ahora.
password.expiration.warning=Su contraseña caduca en {0} días. Por favor <a href="/console/account/changePassword">cambie su contraseña</a> ahora.
password.expiration.loginsRemaining=Tiene {0} inicios de sesión restantes antes que <strong>DEBE</strong> cambiar su contraseña.
screen.accountdisabled.heading=Se ha deshabilitado esta cuenta.
screen.accountdisabled.message=Por favor contacte al administrador de sistema para recobrar acceso.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/messages_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ screen.surrogates.button.cancel=Annuler


# Password policy
password.expiration.warning=Votre mot de passe expire dans {0} jour(s). Merci de <a href="{1}">changer votre mot de passe</a> maintenant.
password.expiration.warning=Votre mot de passe expire dans {0} jour(s). Merci de <a href="/console/account/changePassword">changer votre mot de passe</a> maintenant.
password.expiration.loginsRemaining=Il vous reste {0} authentification(s) avant de <strong>DEVOIR</strong> changer votre mot de passe.
screen.accountdisabled.heading=Ce compte a été désactivé.
screen.accountdisabled.message=Merci de contacter votre administrateur système pour récupérer votre accès.
Expand Down
165 changes: 165 additions & 0 deletions src/main/resources/templates/georchestra/fragments/pwdupdateform.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

<title>Password Update Form</title>
<link href="../../static/css/cas.css" rel="stylesheet" th:remove="tag"/>
</head>

<body>
<main role="main" class="container mt-3 mb-3">
<div id="pwdmain" th:fragment="pwdupdateform">
<script th:inline="javascript">
/*<![CDATA[*/

var policyPattern = /*[[${passwordPolicyPattern}]]*/;
var passwordStrengthI18n = {
0: /*[[#{screen.pm.password.strength.0}]]*/,
1: /*[[#{screen.pm.password.strength.1}]]*/,
2: /*[[#{screen.pm.password.strength.2}]]*/,
3: /*[[#{screen.pm.password.strength.3}]]*/,
4: /*[[#{screen.pm.password.strength.4}]]*/
};
var passwordMinimumStrength = 0;

/*]]>*/
</script>
<h3 th:utext="${expiredPass}
? #{screen.expiredpass.heading(${credential.id})}
: #{screen.mustchangepass.heading(${credential.id})}">
Change Password Heading</h3>

<form id="passwordManagementForm" th:if="${passwordManagementEnabled}" method="post"
th:object="${password}">
<div class="banner banner-danger alert alert-danger banner-dismissible my-4" th:if="${#fields.hasErrors('*')}">

<span th:each="err : ${#fields.errors('*')}" th:utext="${err}">Error text</span>
</div>

<div class="cas-field my-3 mdc-input-group form-group d-flex">
<div class="mdc-input-group-field mdc-input-group-field-append flex-grow-1">
<div class="d-flex caps-check">

<label for="password"
class="mdc-text-field caps-check mdc-text-field--outlined control-label mdc-text-field--with-trailing-icon">
<input class="mdc-text-field__input form-control pwd"
autocapitalize="none"
spellcheck="false"
type="password"
name="password"
id="password"
th:field="*{password}"
autocomplete="off" required/>
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" th:utext="#{screen.pm.enterpsw}">Enter Password:</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
</label>
<i class="mdi mdi-alert mdc-text-field__icon text-danger caps-warn"></i>
</div>
</div>
<button class="reveal-password align-self-end mdc-button mdc-button--raised mdc-input-group-append mdc-icon-button btn btn-primary"
tabindex="-1" type="button">
<i class="mdi mdi-eye reveal-password-icon fas fa-eye"></i>
<span class="visually-hidden">Toggle Password</span>
</button>
</div>

<div class="cas-field form-group mdc-input-group my-3">
<div class="d-flex caps-check">
<label for="confirmedPassword" class="mdc-text-field mdc-text-field--outlined mdc-text-field--with-trailing-icon control-label">
<input class="mdc-text-field__input form-control pwd"
type="password"
autocapitalize="none"
spellcheck="false"
name="confirmedPassword"
id="confirmedPassword"
th:field="*{confirmedPassword}"
autocomplete="off" required/>

<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" th:utext="#{screen.pm.confirmpsw}">Confirm Password:</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
</label>
<i class="mdi mdi-alert mdc-text-field__icon bs-hide text-danger caps-warn"></i>
</div>
</div>

<div class="cas-field form-group my-3 d-flex flex-column">
<div class="d-flex align-items-center">
<span th:text="#{screen.pm.password.strength}">Strength:</span>&nbsp;
<span id="password-strength-icon" class="mdi" aria-hidden="true"></span>
</div>
<div id="strengthProgressBar" role="progressbar" class="d-none progress">
<div id="progress-strength-indicator" class="progress-bar progress-bar-indicator text-center"></div>
<div class="mdc-linear-progress">
<div class="mdc-linear-progress__buffering-dots"></div>
<div class="mdc-linear-progress__buffer"></div>
<div class="mdc-linear-progress__bar mdc-linear-progress__primary-bar" style="transform: scaleX(0);">
<span class="mdc-linear-progress__bar-inner"></span>
</div>
<div class="mdc-linear-progress__bar mdc-linear-progress__secondary-bar">
<span class="mdc-linear-progress__bar-inner"></span>
</div>
</div>
</div>
</div>
<div class="cas-field form-group my-3 text-warning" id="password-strength-msg" style="display: none;">
<div class="suggestions banner banner-warning alert alert-warning p-2 mb-2 d-flex align-items-center" role="alert">
<span class="mdi mdi-information fas fa-info-circle" aria-hidden="true"></span>&nbsp;
<p class="m-0">
<span id="password-strength-warning"></span>&nbsp;
<span id="password-strength-suggestions"></span>
</p>
</div>
</div>
<div class="cas-field form-group my-3 text-danger" id="password-strength-notes">
<div id="password-policy-violation-msg" class="banner banner-danger alert alert-danger p-2" role="alert" style="display: none;">
<span class="mdi mdi-alert fas fa-exclamation-triangle" aria-hidden="true"></span>&nbsp;
<strong th:text="#{screen.pm.password.policyViolation}">Password does not match the password policy
requirement.</strong>
</div>
<div id="password-confirm-mismatch-msg" class="banner banner-danger alert alert-danger p-2" role="alert" style="display: none;">
<span class="mdi mdi-alert fas fa-exclamation-triangle" aria-hidden="true"></span>&nbsp;
<strong th:text="#{screen.pm.password.confirmMismatch}">Passwords do not match.</strong>
</div>
</div>

<div class="cas-field form-group">
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="submit"/>
<button class="mdc-button mdc-button--raised btn btn-primary me-2"
name="submit"
accesskey="s"
th:value="#{screen.pm.button.submit}"
th:attr="data-processing-text=#{screen.welcome.button.loginwip}"
value="SUBMIT"
id="submit"
type="submit"
disabled="true">
<span class="mdc-button__label" th:text="#{screen.pm.button.submit}">Submit</span>
</button>
<a class="mdc-button mdc-button--outline btn btn-outline-secondary" th:href="@{/login}">
<span class="mdc-button__label" th:text="#{screen.pm.button.cancel}">CANCEL</span>
</a>
</div>
</form>

<p id="pwddesc" th:unless="${passwordManagementEnabled}"
th:utext="${expiredPass} ? #{screen.expiredpass.message('/console/account/passwordRecovery')} : #{screen.mustchangepass.message('/console/account/passwordRecovery')}">Expired/Must Change Password text</p>

</div>
</main>
</body>
</html>

0 comments on commit 4b18b6c

Please sign in to comment.