This article will demonstrate how to create a custom password policy in the Apereo CAS Identity Provider (IDP). This is their official documentation: https://apereo.github.io/cas/7.1.x/installation/Password-Policy-Enforcement.html. This article will not regurgitate the default password policy enforcement options provided in that documentation but rather demonstrate how to customize a unique password policy with a groovy script.
Authentication Handler properties
This section presumes you already have an authentication handler defined. In our example we are using an ldap authentication handler against Active Directory (AD). This example also uses a cas.groovy file instead of cas.properties so you will need to adjust your property syntax to fit your property file type.
cas.authn.ldap["0"].passwordPolicy.enabled="true"
cas.authn.ldap["0"].passwordPolicy.strategy="GROOVY"
cas.authn.ldap["0"].passwordPolicy.groovy.location="file:/etc/cas/config/groovy/password-policy.groovy"The script we will write below makes use of the pwdLastSet attribute. CAS doesn’t treat this attribute as a principal attribute so it must be configured as an additional attribute.
cas.authn.ldap["0"].additionalAttributes="pwdLastSet"Then, the additional attributes must be merged.
cas.authn.attributeRepository.core.merger="ADD"Password Policy Groovy Script
The script blends Java with Groovy and should be modified to fit your specific use case. Please leave us a comment with improvements to the script. Note that its location should be the same as what was set in the previous section with the passwordPolicy.groovy.location property.
If you want to inspect the attributes being returned by your ldap authentication handler you can log or debug the response.ldapEntry object. This object is part of the ldaptive library.
Some key customizations of this script are as follows:
- Use the pwdLastSet AD attribute and convert to a java Date
- Compare this Date with the current date
- Show an expiration warning after a user successfully logs in
- DefaultMessageDescriptor returns the simple message screen
- PasswordExpiringWarningMessageDescriptor is used to return a message with a days remaining variable
import java.util.*
import java.text.*
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import org.ldaptive.auth.*
import org.apereo.cas.*
import org.apereo.cas.authentication.*
import org.apereo.cas.authentication.support.*
import org.apereo.cas.authentication.support.password.*
List<MessageDescriptor> run(final Object... args) {
def (response,configuration,logger,applicationContext) = args
logger.info("Handling password policy [{}] via ${configuration.getAccountStateHandler()}", response)
def pwdLastSet = response.ldapEntry.getAttribute('pwdLastSet').getStringValue()
long pwdSetDate = java.lang.Long.parseLong(pwdLastSet)
long timeAdjust = 11644473600000L // adjust factor for converting it to java
Date pwdSet = new Date((pwdSetDate / 10000 - timeAdjust).toLong())
now = ZonedDateTime.now()
expirationDate = ZonedDateTime.ofInstant(pwdSet.toInstant(), ZoneId.systemDefault()).plusDays(365)
days = ChronoUnit.DAYS.between(now.toLocalDate(), expirationDate.toLocalDate())
if (now.toInstant().isAfter(expirationDate.toInstant())) {
return [new DefaultMessageDescriptor("cas.custom.change.password")] as List<MessageDescriptor>
} else if (now.toInstant().isAfter(expirationDate.minusDays(14).toInstant())) {
return [new PasswordExpiringWarningMessageDescriptor("", days)] as List<MessageDescriptor>
}
def accountStateHandler = configuration.getAccountStateHandler()
return accountStateHandler.handle(response, configuration)
}Two custom messages are configured in src/main/resources/custom_messages.properties in your cas-overlay. Note that {0} is a variable and will be replaced with the number of days computed in the groovy script.
password.expiration.warning=Your password expires in {0} day(s). Please <a href="https://password.example.com">change your password</a> now or click Continue to proceed to the service you are logging in to.
cas.custom.change.password=<br/>Your password has expired. You must change your password now.Conclusion – Apereo CAS Custom Password Policy
This article has demonstrated how to create a custom password policy to be enforced in Apereo CAS. Leave us a comment with any feedback or questions. If you found this article helpful, see more of our Apereo CAS articles.
Leave a Reply