Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passwordExpiryTime in user claims on request #856

Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class PasswordPolicyConstants {
public static final String AUTHENTICATION_STATUS = "authenticationStatus";
public static final String BASIC_AUTHENTICATOR = "BasicAuthenticator";
public static final String FALSE = "false";
public static final String TRUE = "true";
public static final String CONFIRMATION_QUERY_PARAM = "&confirmation=";
public static final String PASSWORD_EXPIRED_QUERY_PARAMS = "&passwordExpired=true";
public static final String PASSWORD_EXPIRED_MSG_QUERY_PARAM = "&passwordExpiredMsg=";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -159,6 +161,8 @@ public static boolean isPasswordExpired(String tenantDomain, String tenantAwareU
throws PostAuthenticationFailedException {

try {
if (!isPasswordExpiryEnabled(tenantDomain)) return false;

UserRealm userRealm = getUserRealm(tenantDomain);
UserStoreManager userStoreManager = getUserStoreManager(userRealm);
String userId = ((AbstractUserStoreManager) userStoreManager).getUserIDFromUserName(tenantAwareUsername);
Expand All @@ -176,11 +180,8 @@ public static boolean isPasswordExpired(String tenantDomain, String tenantAwareU
skipIfNoApplicableRules);
}

// If the default behavior is to skip the password expiry, rules with skip logic are not necessary.
List<PasswordExpiryRule> filteredRules = passwordExpiryRules.stream()
.filter(rule -> !skipIfNoApplicableRules ||
!PasswordExpiryRuleOperatorEnum.NE.equals(rule.getOperator()))
.collect(Collectors.toList());
List<PasswordExpiryRule> filteredRules =
filterApplicableExpiryRules(passwordExpiryRules, skipIfNoApplicableRules);

Map<PasswordExpiryRuleAttributeEnum, Set<String>> fetchedUserAttributes =
new EnumMap<>(PasswordExpiryRuleAttributeEnum.class);
Expand All @@ -193,7 +194,7 @@ public static boolean isPasswordExpired(String tenantDomain, String tenantAwareU
}
int expiryDays =
rule.getExpiryDays() > 0 ? rule.getExpiryDays() : getPasswordExpiryInDays(tenantDomain);
return daysDifference >= expiryDays || lastPasswordUpdatedTime == null;
return daysDifference >= expiryDays || StringUtils.isBlank(lastPasswordUpdatedTime);
}
}
// Apply default password expiry policy if no specific rule applies.
Expand Down Expand Up @@ -292,7 +293,112 @@ private static boolean isPasswordExpiredUnderDefaultPolicy(String tenantDomain,
throws PostAuthenticationFailedException {

if (skipIfNoApplicableRules) return false;
return lastPasswordUpdatedTime == null || daysDifference >= getPasswordExpiryInDays(tenantDomain);
return StringUtils.isBlank(lastPasswordUpdatedTime) || daysDifference >= getPasswordExpiryInDays(tenantDomain);
}

/**
* This method returns password expiry time for the given user.
*
* @param tenantDomain The tenant domain.
* @param tenantAwareUsername The tenant aware username.
* @param groupIds The group IDs of the user.
* @param roleIds The role IDs of the user.
* @return Optional containing the password expiry time in milliseconds, or empty if not applicable.
* @throws PostAuthenticationFailedException If an error occurred while getting the password expiry time.
*/
public static Optional<Long> getUserPasswordExpiryTime(String tenantDomain, String tenantAwareUsername,
List<String> groupIds, List<String> roleIds)
throws PostAuthenticationFailedException {

try {
// If the password expiry is not enabled, password expiry time is not applicable.
if (!isPasswordExpiryEnabled(tenantDomain)) return Optional.empty();

UserRealm userRealm = getUserRealm(tenantDomain);
UserStoreManager userStoreManager = getUserStoreManager(userRealm);
String userId = ((AbstractUserStoreManager) userStoreManager).getUserIDFromUserName(tenantAwareUsername);
String lastPasswordUpdatedTime =
getLastPasswordUpdatedTime(tenantAwareUsername, userStoreManager, userRealm);

long lastPasswordUpdatedTimeInMillis = 0L;
boolean isLastPasswordUpdatedTimeBlank = StringUtils.isBlank(lastPasswordUpdatedTime);
if (!isLastPasswordUpdatedTimeBlank) {
lastPasswordUpdatedTimeInMillis = getLastPasswordUpdatedTimeInMillis(lastPasswordUpdatedTime);
}
int defaultPasswordExpiryInDays = getPasswordExpiryInDays(tenantDomain);
boolean skipIfNoApplicableRules = isSkipIfNoApplicableRulesEnabled(tenantDomain);

List<PasswordExpiryRule> passwordExpiryRules = getPasswordExpiryRules(tenantDomain);

// If no rules are defined, use the default expiry time if "skipIfNoApplicableRules" is disabled.
if (CollectionUtils.isEmpty(passwordExpiryRules)) {
if (skipIfNoApplicableRules) return Optional.empty();
// If lastPasswordUpdatedTime is blank, set expiry time to now.
if (isLastPasswordUpdatedTimeBlank) {
return Optional.of(System.currentTimeMillis());
}
return Optional.of(
lastPasswordUpdatedTimeInMillis + getDaysTimeInMillis(defaultPasswordExpiryInDays));
}

List<PasswordExpiryRule> filteredRules =
filterApplicableExpiryRules(passwordExpiryRules, skipIfNoApplicableRules);

Map<PasswordExpiryRuleAttributeEnum, Set<String>> userAttributes =
new EnumMap<>(PasswordExpiryRuleAttributeEnum.class);
if (groupIds != null) {
userAttributes.put(PasswordExpiryRuleAttributeEnum.GROUPS, new HashSet<>(groupIds));
}
if (roleIds != null) {
userAttributes.put(PasswordExpiryRuleAttributeEnum.ROLES, new HashSet<>(roleIds));
}

for (PasswordExpiryRule rule : filteredRules) {
if (isRuleApplicable(rule, userAttributes, tenantDomain, userId, userStoreManager)) {
// Skip the rule if the operator is not equals.
if (PasswordExpiryRuleOperatorEnum.NE.equals(rule.getOperator())) {
return Optional.empty();
}
if (isLastPasswordUpdatedTimeBlank) {
return Optional.of(System.currentTimeMillis());
}
int expiryDays =
rule.getExpiryDays() > 0 ? rule.getExpiryDays() : getPasswordExpiryInDays(tenantDomain);
return Optional.of(lastPasswordUpdatedTimeInMillis + getDaysTimeInMillis(expiryDays));
}
}

if (skipIfNoApplicableRules) return Optional.empty();
if (isLastPasswordUpdatedTimeBlank) {
return Optional.of(System.currentTimeMillis());
}
return Optional.of(
lastPasswordUpdatedTimeInMillis + getDaysTimeInMillis(defaultPasswordExpiryInDays));
} catch (UserStoreException e) {
throw new PostAuthenticationFailedException(PasswordPolicyConstants.ErrorMessages.
ERROR_WHILE_GETTING_USER_STORE_DOMAIN.getCode(),
PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_GETTING_USER_STORE_DOMAIN.getMessage());
}
}

private static List<PasswordExpiryRule> filterApplicableExpiryRules(List<PasswordExpiryRule> passwordExpiryRules,
boolean skipIfNoApplicableRules) {

// If the default behavior is to skip the password expiry, rules with skip logic are not required.
return passwordExpiryRules.stream().filter(
rule -> !skipIfNoApplicableRules || !PasswordExpiryRuleOperatorEnum.NE.equals(rule.getOperator()))
PasinduYeshan marked this conversation as resolved.
Show resolved Hide resolved
.collect(Collectors.toList());
}

/**
* This method returns the time in milliseconds for the given number of days.
*
* @param days The number of days.
* @return The time in milliseconds.
*/
private static long getDaysTimeInMillis(int days) {

return (long) days * 24 * 60 * 60 * 1000;
}

/**
Expand Down
Loading
Loading