diff --git a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/impl/SCIMUserManager.java b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/impl/SCIMUserManager.java index efab848c8..6f5a23dba 100644 --- a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/impl/SCIMUserManager.java +++ b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/impl/SCIMUserManager.java @@ -41,7 +41,9 @@ import org.wso2.carbon.identity.claim.metadata.mgt.util.ClaimConstants; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.event.IdentityEventConstants; import org.wso2.carbon.identity.event.IdentityEventException; +import org.wso2.carbon.identity.event.event.Event; import org.wso2.carbon.identity.mgt.policy.PolicyViolationException; import org.wso2.carbon.identity.provisioning.IdentityProvisioningConstants; import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException; @@ -56,6 +58,7 @@ import org.wso2.carbon.identity.scim2.common.utils.AttributeMapper; import org.wso2.carbon.identity.scim2.common.utils.SCIMCommonConstants; import org.wso2.carbon.identity.scim2.common.utils.SCIMCommonUtils; +import org.wso2.carbon.identity.scim2.common.utils.Scenarios; import org.wso2.carbon.user.api.ClaimMapping; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.PaginatedUserStoreManager; @@ -1114,6 +1117,7 @@ public User updateUser(User user, Map requiredAttributes) throw // If password is updated, set it separately. if (user.getPassword() != null) { carbonUM.updateCredentialByAdminWithID(user.getId(), user.getPassword()); + publishEvent(user, IdentityEventConstants.Event.POST_UPDATE_CREDENTIAL_BY_SCIM, false); } updateUserClaims(user, oldClaimList, claimValuesInLocalDialect); @@ -1299,6 +1303,7 @@ public User updateUser(User user, Map requiredAttributes, // If password is updated, set it separately. if (user.getPassword() != null) { carbonUM.updateCredentialByAdminWithID(user.getId(), user.getPassword()); + publishEvent(user, IdentityEventConstants.Event.POST_UPDATE_CREDENTIAL_BY_SCIM, true); } updateUserClaims(user, oldClaimList, claimValuesInLocalDialect, allSimpleMultiValuedClaimsList); @@ -6272,4 +6277,41 @@ private List addDomainToNames(String userStoreDomainName, List g return groupsList.stream().map(groupName -> UserCoreUtil.addDomainToName(groupName, userStoreDomainName)) .collect(Collectors.toList()); } + + /** + * Publish event for credential updates. + * + * @param user User object. + * @param eventName Name of the event. + * @param isAdminUpdate Indicates whether the user is an admin. + * @throws BadRequestException If the request is invalid. + * @throws UserStoreException If an error occurs related to the user store. + * @throws CharonException If an error occurs during the event handling. + */ + private void publishEvent(User user, String eventName, boolean isAdminUpdate) + throws BadRequestException, UserStoreException, CharonException { + + HashMap properties = new HashMap<>(); + properties.put(IdentityEventConstants.EventProperty.USER_NAME, + UserCoreUtil.removeDomainFromName(user.getUsername())); + properties.put(IdentityEventConstants.EventProperty.TENANT_DOMAIN, tenantDomain); + properties.put(IdentityEventConstants.EventProperty.TENANT_ID, carbonUM.getTenantId()); + properties.put(IdentityEventConstants.EventProperty.USER_STORE_DOMAIN, + IdentityUtil.extractDomainFromName(user.getUsername())); + properties.put(IdentityEventConstants.EventProperty.CREDENTIAL, user.getPassword()); + if (isAdminUpdate) { + properties.put(IdentityEventConstants.EventProperty.SCENARIO, + Scenarios.CREDENTIAL_UPDATE_BY_ADMIN_VIA_CONSOLE.name()); + } else { + properties.put(IdentityEventConstants.EventProperty.SCENARIO, + Scenarios.CREDENTIAL_UPDATE_BY_USER_VIA_MY_ACCOUNT.name()); + } + + Event identityMgtEvent = new Event(eventName, properties); + try { + SCIMCommonComponentHolder.getIdentityEventService().handleEvent(identityMgtEvent); + } catch (IdentityEventException e) { + throw new BadRequestException("Error occurred publishing event", ResponseCodeConstants.INVALID_VALUE); + } + } } diff --git a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponent.java b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponent.java index 50b59bb77..2dd32edc3 100644 --- a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponent.java +++ b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponent.java @@ -32,6 +32,7 @@ import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.handler.AbstractEventHandler; +import org.wso2.carbon.identity.event.services.IdentityEventService; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.role.mgt.core.RoleManagementService; import org.wso2.carbon.identity.scim2.common.extenstion.SCIMUserStoreErrorResolver; @@ -361,6 +362,32 @@ protected void unsetOrganizationManager(OrganizationManager organizationManager) SCIMCommonComponentHolder.setOrganizationManager(null); } + /** + * Unset identityEventService service implementation. + * + * @param identityEventService IdentityEventService + */ + protected void unsetIdentityEventService(IdentityEventService identityEventService) { + + SCIMCommonComponentHolder.setIdentityEventService(null); + } + + /** + * Set IdentityEventService implementation + * + * @param identityEventService IdentityEventService + */ + @Reference( + name = "IdentityEventService", + service = org.wso2.carbon.identity.event.services.IdentityEventService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetIdentityEventService") + protected void setIdentityEventService(IdentityEventService identityEventService) { + + SCIMCommonComponentHolder.setIdentityEventService(identityEventService); + } + @Deactivate protected void deactivate(ComponentContext context) { diff --git a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponentHolder.java b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponentHolder.java index b8f803b28..2a3459f8c 100644 --- a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponentHolder.java +++ b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/internal/SCIMCommonComponentHolder.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.scim2.common.internal; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService; +import org.wso2.carbon.identity.event.services.IdentityEventService; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.scim2.common.extenstion.SCIMUserStoreErrorResolver; import org.wso2.carbon.idp.mgt.IdpManager; @@ -43,6 +44,7 @@ public class SCIMCommonComponentHolder { private static org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService roleManagementServiceV2; private static OrganizationManager organizationManager; private static IdpManager idpManager; + private static IdentityEventService identityEventService; private static final List scimUserStoreErrorResolvers = new ArrayList<>(); /** @@ -203,4 +205,24 @@ public static void removeScimUserStoreErrorResolver(SCIMUserStoreErrorResolver s scimUserStoreErrorResolvers.remove(scimUserStoreErrorResolver); scimUserStoreErrorResolvers.sort(Comparator.comparing(SCIMUserStoreErrorResolver::getOrder).reversed()); } + + /** + * Get IdentityEvent service. + * + * @return IdentityEventService. + */ + public static IdentityEventService getIdentityEventService() { + + return identityEventService; + } + + /** + * Set IdentityEvent service. + * + * @param identityEventService IdentityEventService. + */ + public static void setIdentityEventService(IdentityEventService identityEventService) { + + SCIMCommonComponentHolder.identityEventService = identityEventService; + } } diff --git a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/SCIMCommonConstants.java b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/SCIMCommonConstants.java index 8881f1f9b..b372bd78b 100644 --- a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/SCIMCommonConstants.java +++ b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/SCIMCommonConstants.java @@ -179,7 +179,7 @@ public enum ErrorMessages { ERROR_CODE_REGEX_VIOLATION("SUO-10001", "Regex validation error", "%s attribute value doesn't match with %s regex pattern"), ERROR_CODE_LENGTH_VIOLATION("SUO-10002", "Length validation error", - "%s attribute should be between %s and %s characters"); + "%s attribute should be between %s and %s characters"); private final String code; private final String message; diff --git a/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/Scenarios.java b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/Scenarios.java new file mode 100644 index 000000000..8ba5d2d52 --- /dev/null +++ b/components/org.wso2.carbon.identity.scim2.common/src/main/java/org/wso2/carbon/identity/scim2/common/utils/Scenarios.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.scim2.common.utils; + +import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.identity.scim2.common.exceptions.IdentitySCIMException; + +/** + * Enum which contains the scenarios. + */ +public enum Scenarios { + + CREDENTIAL_UPDATE_BY_ADMIN_VIA_CONSOLE, + CREDENTIAL_UPDATE_BY_USER_VIA_MY_ACCOUNT; + + /** + * Get scenario which matches the given scenario name. + * + * @param scenarioName Name of the scenario + * @return Scenarios + * @throws IdentitySCIMException Invalid scenario + */ + public static Scenarios getScenario(String scenarioName) throws IdentitySCIMException { + + Scenarios[] scenarios = { + CREDENTIAL_UPDATE_BY_ADMIN_VIA_CONSOLE, CREDENTIAL_UPDATE_BY_USER_VIA_MY_ACCOUNT + }; + if (StringUtils.isNotEmpty(scenarioName)) { + for (Scenarios scenario : scenarios) { + if (scenarioName.equals(scenario.name())) { + return scenario; + } + } + } + throw new IdentitySCIMException("Invalid scenario: " + scenarioName); + } + +} diff --git a/pom.xml b/pom.xml index 1363b7cf6..39b5fa2d2 100644 --- a/pom.xml +++ b/pom.xml @@ -285,7 +285,7 @@ 6.5.3 3.2.0.wso2v1 4.10.2 - 5.25.612 + 7.0.89 4.13.1 20030203.000129 1.8.12