diff --git a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/constants/PasswordPolicyConstants.java b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/constants/PasswordPolicyConstants.java index e7641dcab6..9bd3acffe6 100644 --- a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/constants/PasswordPolicyConstants.java +++ b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/constants/PasswordPolicyConstants.java @@ -60,6 +60,7 @@ public class PasswordPolicyConstants { public static final String CONFIRMATION_QUERY_PARAM = "&confirmation="; public static final String PASSWORD_EXPIRED_QUERY_PARAMS = "&passwordExpired=true"; public static final String PASSWORD_EXPIRY_RULES_PREFIX = "passwordExpiry.rule"; + public static final Integer MAX_PASSWORD_EXPIRY_RULE_VALUES = 5; public enum ErrorMessages { ERROR_WHILE_GETTING_USER_STORE_DOMAIN("80001", diff --git a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/models/PasswordExpiryRule.java b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/models/PasswordExpiryRule.java index aa548f83c3..acd11f5afc 100644 --- a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/models/PasswordExpiryRule.java +++ b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/models/PasswordExpiryRule.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.password.expiry.models; import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants; import java.util.ArrayList; import java.util.List; @@ -40,7 +41,7 @@ public PasswordExpiryRule(String rule) throws IllegalArgumentException{ try { // Rule format: "priority,expiryDays,attribute,operator,value1,value2, ...". // At least 5 parts are required in the rule definition. - int ruleSectionLength = 4; + int ruleSectionLength = 5; String[] ruleSections = rule.split(RULE_SPLIT_REGEX); if (ruleSections.length < ruleSectionLength) { @@ -49,6 +50,15 @@ public PasswordExpiryRule(String rule) throws IllegalArgumentException{ this.priority = Integer.parseInt(ruleSections[0].trim()); this.expiryDays = Integer.parseInt(ruleSections[1].trim()); + + if (this.priority <= 0) { + throw new IllegalArgumentException("Invalid rule format: priority should be positive."); + } + // Expiry days can be 0 in skip password expiry scenarios. + if (this.expiryDays < 0) { + throw new IllegalArgumentException("Invalid rule format: expiry days should be positive."); + } + this.attribute = PasswordExpiryRuleAttributeEnum.fromString(ruleSections[2].trim()); this.operator = PasswordExpiryRuleOperatorEnum.fromString(ruleSections[3].trim()); @@ -66,6 +76,10 @@ public PasswordExpiryRule(String rule) throws IllegalArgumentException{ if (this.values.isEmpty()) { throw new IllegalArgumentException("Invalid rule format: no valid values provided."); } + if (this.values.size() > PasswordPolicyConstants.MAX_PASSWORD_EXPIRY_RULE_VALUES) { + throw new IllegalArgumentException("Invalid rule format: number of values exceeds the maximum limit of " + + PasswordPolicyConstants.MAX_PASSWORD_EXPIRY_RULE_VALUES + "."); + } } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid rule format: " + e.getMessage()); } diff --git a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/util/PasswordPolicyUtils.java b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/util/PasswordPolicyUtils.java index 3a361173d4..8469fde2ec 100644 --- a/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/util/PasswordPolicyUtils.java +++ b/components/org.wso2.carbon.identity.password.expiry/src/main/java/org/wso2/carbon/identity/password/expiry/util/PasswordPolicyUtils.java @@ -250,25 +250,28 @@ private static Set getUserAttributes(PasswordExpiryRuleAttributeEnum att throws PostAuthenticationFailedException { if (!fetchedUserAttributes.containsKey(attribute)) { - try { - switch (attribute) { - case ROLES: - List userRoles = getUserRoles(tenantDomain, userId); - Set userRoleIds = userRoles.stream().map(RoleBasicInfo::getId).collect(Collectors.toSet()); - fetchedUserAttributes.put(PasswordExpiryRuleAttributeEnum.ROLES, userRoleIds); - break; - case GROUPS: - List userGroups = - ((AbstractUserStoreManager) userStoreManager).getGroupListOfUser(userId, - null, null); - Set userGroupIds = userGroups.stream().map(Group::getGroupID).collect(Collectors.toSet()); + switch (attribute) { + case ROLES: + // Fetch roles assigned to user via groups. + Set userGroupIds; + if (fetchedUserAttributes.containsKey(PasswordExpiryRuleAttributeEnum.GROUPS)) { + userGroupIds = fetchedUserAttributes.get(PasswordExpiryRuleAttributeEnum.GROUPS); + } else { + userGroupIds = getUserGroupIds(userId, userStoreManager); fetchedUserAttributes.put(PasswordExpiryRuleAttributeEnum.GROUPS, userGroupIds); - break; - } - } catch (UserStoreException e) { - throw new PostAuthenticationFailedException(PasswordPolicyConstants.ErrorMessages. - ERROR_WHILE_RETRIEVING_USER_GROUPS.getCode(), - PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_RETRIEVING_USER_GROUPS.getMessage()); + } + List roleIdsOfGroups = getRoleIdsOfGroups(new ArrayList<>(userGroupIds), tenantDomain); + + List userRoles = getUserRoles(tenantDomain, userId); + Set userRoleIds = + userRoles.stream().map(RoleBasicInfo::getId).collect(Collectors.toSet()); + userRoleIds.addAll(roleIdsOfGroups); + fetchedUserAttributes.put(PasswordExpiryRuleAttributeEnum.ROLES, userRoleIds); + break; + case GROUPS: + Set groupIds = getUserGroupIds(userId, userStoreManager); + fetchedUserAttributes.put(PasswordExpiryRuleAttributeEnum.GROUPS, groupIds); + break; } } return fetchedUserAttributes.get(attribute); @@ -314,6 +317,51 @@ public static List getUserRoles(String tenantDomain, String userI } } + /** + * Get the group IDs of the given user. + * + * @param userId The user ID. + * @param userStoreManager The user store manager. + * @return The group IDs of the user. + * @throws PostAuthenticationFailedException If an error occurs while getting the group IDs of the user. + */ + private static Set getUserGroupIds(String userId, UserStoreManager userStoreManager) + throws PostAuthenticationFailedException { + + try { + List userGroups = + ((AbstractUserStoreManager) userStoreManager).getGroupListOfUser(userId, + null, null); + return userGroups.stream().map(Group::getGroupID).collect(Collectors.toSet()); + } catch (UserStoreException e) { + throw new PostAuthenticationFailedException(PasswordPolicyConstants.ErrorMessages. + ERROR_WHILE_RETRIEVING_USER_GROUPS.getCode(), + PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_RETRIEVING_USER_GROUPS.getMessage()); + } + } + + /** + * Get the role IDs of the given groups. + * + * @param groupIds The group IDs. + * @param tenantDomain The tenant domain. + * @return The role IDs of the groups. + * @throws PostAuthenticationFailedException If an error occurs while getting the role IDs of the groups. + */ + private static List getRoleIdsOfGroups(List groupIds, String tenantDomain) + throws PostAuthenticationFailedException { + + try { + RoleManagementService roleManagementService = EnforcePasswordResetComponentDataHolder.getInstance() + .getRoleManagementService(); + return roleManagementService.getRoleIdListOfGroups(groupIds, tenantDomain); + } catch (IdentityRoleManagementException e) { + throw new PostAuthenticationFailedException(PasswordPolicyConstants.ErrorMessages. + ERROR_WHILE_RETRIEVING_USER_ROLES.getCode(), + PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_RETRIEVING_USER_ROLES.getMessage()); + } + } + /** * This method retrieves the last password updated time in milliseconds. *