Skip to content

Commit

Permalink
Add password expiry time to SCIM2 users response when requested
Browse files Browse the repository at this point in the history
  • Loading branch information
PasinduYeshan committed Dec 2, 2024
1 parent 7571ccb commit 6728550
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
4 changes: 4 additions & 0 deletions components/org.wso2.carbon.identity.scim2.common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.password.policy</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.password.expiry</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.organization.management.core</groupId>
<artifactId>org.wso2.carbon.identity.organization.management.service</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.identity.application.authentication.framework.exception.PostAuthenticationFailedException;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.ServiceProvider;
Expand Down Expand Up @@ -98,6 +99,7 @@
import org.wso2.charon3.core.objects.Role;
import org.wso2.charon3.core.objects.RoleV2;
import org.wso2.charon3.core.objects.User;
import org.wso2.charon3.core.objects.plainobjects.MultiValuedComplexType;
import org.wso2.charon3.core.objects.plainobjects.UsersGetResponse;
import org.wso2.charon3.core.objects.plainobjects.GroupsGetResponse;
import org.wso2.charon3.core.protocol.ResponseCodeConstants;
Expand All @@ -114,6 +116,8 @@
import org.wso2.charon3.core.utils.codeutils.PatchOperation;
import org.wso2.charon3.core.utils.codeutils.SearchRequest;
import org.wso2.carbon.identity.configuration.mgt.core.model.Resource;
import org.wso2.carbon.identity.password.expiry.util.PasswordPolicyUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import java.time.Instant;
import java.util.AbstractMap;
Expand Down Expand Up @@ -173,6 +177,9 @@ public class SCIMUserManager implements UserManager {
private static final String ENABLE_PAGINATED_USER_STORE = "SCIM.EnablePaginatedUserStore";
private static final String SERVICE_PROVIDER = "serviceProvider";
private final String SERVICE_PROVIDER_TENANT_DOMAIN = "serviceProviderTenantDomain";
private final String PASSWORD_EXPIRY_TIME_ATTRIBUTE_NAME = "passwordExpiryTime";
private final String PASSWORD_EXPIRY_TIME_CLAIM_URI =
getCustomSchemaURI() + SCIMConstants.OperationalConstants.COLON + PASSWORD_EXPIRY_TIME_ATTRIBUTE_NAME;

// Additional wso2 user schema properties.
private static final String DISPLAY_NAME_PROPERTY = "displayName";
Expand Down Expand Up @@ -4188,6 +4195,10 @@ private User getSCIMUser(org.wso2.carbon.user.core.common.User coreUser, List<St
setRolesOfUser(rolesList, groupMetaAttributesCache, coreUser, scimUser);
}

if (scimToLocalClaimsMap.containsKey(PASSWORD_EXPIRY_TIME_CLAIM_URI)) {
setUserPasswordExpiryTime(scimUser, coreUser, tenantDomain);
}

} catch (UserStoreException e) {
throw resolveError(e, "Error in getting user information for user: " +
coreUser.getDomainQualifiedUsername());
Expand Down Expand Up @@ -4389,6 +4400,9 @@ private Set<User> getSCIMUsers(Set<org.wso2.carbon.user.core.common.User> users,
setRolesOfUser(rolesList, groupMetaAttributesCache, user, scimUser);
}

if (requiredAttributes.containsKey(PASSWORD_EXPIRY_TIME_CLAIM_URI)) {
setUserPasswordExpiryTime(scimUser, user, tenantDomain);
}
} catch (UserStoreException e) {
throw resolveError(e, "Error in getting user information for user: " +
maskIfRequired(user.getUsername()));
Expand Down Expand Up @@ -4417,6 +4431,60 @@ private Set<User> getSCIMUsers(Set<org.wso2.carbon.user.core.common.User> users,
return scimUserSet;
}

/**
* Adds password expiry information to the SCIM user object.
*
* @param scimUser The SCIM user object to update.
* @param user The core user object.
* @param tenantDomain The tenant domain.
* @throws CharonException If there's an error during SCIM object construction.
*/
private void setUserPasswordExpiryTime(User scimUser,
org.wso2.carbon.user.core.common.User user,
String tenantDomain) throws CharonException {

try {
String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(
user.getFullQualifiedUsername());

List<String> roleIds = extractValueFromComplexType(scimUser.getRoles());
List<String> groupIds = extractValueFromComplexType(scimUser.getGroups());

Optional<Long> passwordExpiryTime = PasswordPolicyUtils.getUserPasswordExpiryTime(
tenantDomain, tenantAwareUsername, roleIds, groupIds);

if (passwordExpiryTime.isPresent()) {
Map.Entry<String, String> passwordExpiryTimeEntry = new AbstractMap.SimpleEntry<>(
PASSWORD_EXPIRY_TIME_CLAIM_URI, String.valueOf(passwordExpiryTime.get()));
AttributeMapper.constructSCIMObjectFromAttributesOfLevelTwo(this, passwordExpiryTimeEntry,
scimUser,new String[]{getCustomSchemaURI(), PASSWORD_EXPIRY_TIME_ATTRIBUTE_NAME},
1);
}
} catch (CharonException | NotFoundException | BadRequestException
| PostAuthenticationFailedException e) {
throw new CharonException("Error in getting user information for user: " +
maskIfRequired(user.getUsername()), e);
}
}

/**
* Extracts values from MultiValuedComplexType list.
*
* @param complexTypes List of complex types.
* @return List of extracted values or null if no valid values exist.
*/
private List<String> extractValueFromComplexType(List<MultiValuedComplexType> complexTypes) {

if (CollectionUtils.isEmpty(complexTypes)) {
return null;
}
List<String> values = complexTypes.stream()
.map(MultiValuedComplexType::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList());
return values.isEmpty() ? null : values;
}

private void setRolesOfUser(List<String> rolesOfUser, Map<String, Group> groupMetaAttributesCache,
org.wso2.carbon.user.core.common.User user,
User scimUser) throws org.wso2.carbon.user.core.UserStoreException, CharonException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@
import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.password.expiry.util.PasswordPolicyUtils;
import org.wso2.carbon.identity.scim2.common.DAO.GroupDAO;
import org.wso2.carbon.identity.scim2.common.extenstion.SCIMUserStoreErrorResolver;
import org.wso2.carbon.identity.scim2.common.group.SCIMGroupHandler;
import org.wso2.carbon.identity.scim2.common.internal.SCIMCommonComponentHolder;
import org.wso2.carbon.user.core.UserStoreClientException;
import org.wso2.carbon.user.core.common.PaginatedUserResponse;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.charon3.core.exceptions.NotImplementedException;
import org.wso2.charon3.core.extensions.UserManager;
import org.wso2.charon3.core.objects.plainobjects.UsersGetResponse;
Expand Down Expand Up @@ -102,6 +104,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -206,6 +209,8 @@ public class SCIMUserManagerTest {
private MockedStatic<ClaimMetadataHandler> claimMetadataHandler;
private MockedStatic<CarbonConstants> carbonConstants;
private MockedStatic<IdentityTenantUtil> identityTenantUtil;
private MockedStatic<PasswordPolicyUtils> passwordPolicyUtils;
private MockedStatic<MultitenantUtils> muliTenantUtils;
private MockedStatic<ApplicationManagementService> applicationManagementServiceMockedStatic;
private MockedStatic<SCIMCommonComponentHolder> scimCommonComponentHolder;
private MockedStatic<ResourceManagerUtil> resourceManagerUtil;
Expand All @@ -218,6 +223,8 @@ public void setUpMethod() {
scimCommonUtils = mockStatic(SCIMCommonUtils.class);
carbonConstants = mockStatic(CarbonConstants.class);
identityTenantUtil = mockStatic(IdentityTenantUtil.class);
muliTenantUtils = mockStatic(MultitenantUtils.class);
passwordPolicyUtils = mockStatic(PasswordPolicyUtils.class);
applicationManagementServiceMockedStatic = mockStatic(ApplicationManagementService.class);
scimCommonComponentHolder = mockStatic(SCIMCommonComponentHolder.class);
scimUserSchemaExtensionBuilder = mockStatic(SCIMUserSchemaExtensionBuilder.class);
Expand All @@ -234,6 +241,8 @@ public void tearDown() {
scimCommonUtils.close();
carbonConstants.close();
identityTenantUtil.close();
muliTenantUtils.close();
passwordPolicyUtils.close();
applicationManagementServiceMockedStatic.close();
scimCommonComponentHolder.close();
scimUserSchemaExtensionBuilder.close();
Expand Down Expand Up @@ -744,6 +753,10 @@ public void testFilteringUsersWithGETWithPagination(List<org.wso2.carbon.user.co
scimCommonUtils.when(() -> SCIMCommonUtils.isConsiderMaxLimitForTotalResultEnabled())
.thenReturn(isConsiderMaxLimitForTotalResultEnabled);

muliTenantUtils.when(() -> MultitenantUtils.getTenantAwareUsername(any())).thenReturn("testUser1");
passwordPolicyUtils.when(() ->PasswordPolicyUtils.getUserPasswordExpiryTime(
anyString(), anyString(), any(), any())).thenReturn(Optional.empty());

Map<String, String> supportedByDefaultProperties = new HashMap<String, String>() {{
put("SupportedByDefault", "true");
put("ReadOnly", "true");
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@
<artifactId>org.wso2.carbon.identity.password.policy</artifactId>
<version>${identity.governance.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.password.expiry</artifactId>
<version>${identity.governance.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.recovery</artifactId>
Expand Down

0 comments on commit 6728550

Please sign in to comment.