/* * Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.scim.common.listener; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.identity.core.AbstractIdentityUserOperationEventListener; import org.wso2.carbon.identity.core.util.IdentityCoreConstants; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.scim.common.group.SCIMGroupHandler; import org.wso2.carbon.identity.scim.common.utils.IdentitySCIMException; import org.wso2.carbon.identity.scim.common.utils.SCIMCommonUtils; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserStoreException; import org.wso2.carbon.user.core.UserStoreManager; import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.charon.core.schema.SCIMConstants; import org.wso2.charon.core.util.AttributeUtil; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * This is to perform SCIM related operation on User Operations. * For eg: when a user is created through UserAdmin API, we need to set some SCIM specific properties * as user attributes. */ public class SCIMUserOperationListener extends AbstractIdentityUserOperationEventListener { private static Log log = LogFactory.getLog(SCIMUserOperationListener.class); @Override public int getExecutionOrderId() { int orderId = getOrderId(); if (orderId != IdentityCoreConstants.EVENT_LISTENER_ORDER_ID) { return orderId; } return 90; } @Override public boolean doPreAuthenticate(String s, Object o, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostAuthenticate(String userName, boolean authenticated, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } String domainName = userStoreManager.getRealmConfiguration().getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); if(authenticated){ if (StringUtils.isNotEmpty(UserCoreUtil.getDomainFromThreadLocal())) { if(!StringUtils.equals(UserCoreUtil.getDomainFromThreadLocal(), domainName)){ return true; } } else if (!StringUtils.equals(UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME, domainName)){ return true; } } else { String usernameWithDomain = UserCoreUtil.addDomainToName(userName, domainName); boolean isUserExistInCurrentDomain = userStoreManager.isExistingUser(usernameWithDomain); if (!isUserExistInCurrentDomain) { if (log.isDebugEnabled()) { log.debug("User, " + userName + " does not exist in " + domainName); } return true; } } try { String activeAttributeValue = userStoreManager.getUserClaimValue(userName, SCIMConstants.ACTIVE_URI, null); boolean isUserActive = true; if (activeAttributeValue != null) { isUserActive = Boolean.parseBoolean(activeAttributeValue); if (isUserActive) { return true; } else { log.error("Trying to login from an inactive account of user: " + userName); return false; } } return true; } catch (org.wso2.carbon.user.api.UserStoreException e) { if (e.getMessage().contains("UserNotFound")){ if (log.isDebugEnabled()){ log.debug("User " + userName + " not found in user store", e); } return false; } throw new UserStoreException(e); } } @Override public boolean doPreAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profile, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } claims = this.getSCIMAttributes(userName, claims); return true; } @Override public boolean doPostAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profile, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreUpdateCredential(String s, Object o, Object o1, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostUpdateCredential(String userName, Object credential, UserStoreManager userStoreManager) throws UserStoreException { return doPostUpdateCredentialByAdmin(userName, credential, userStoreManager); } @Override public boolean doPreUpdateCredentialByAdmin(String s, Object o, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostUpdateCredentialByAdmin(String userName, Object credential, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } //update last-modified-date try { Date date = new Date(); String lastModifiedDate = AttributeUtil.formatDateTime(date); userStoreManager.setUserClaimValue( userName, SCIMConstants.META_LAST_MODIFIED_URI, lastModifiedDate, null); } catch (org.wso2.carbon.user.api.UserStoreException e) { if (e.getMessage().contains("UserNotFound")) { if (log.isDebugEnabled()) { log.debug("User " + userName + " doesn't exist"); } } else { throw new UserStoreException("Error updating SCIM metadata in doPostUpdateCredentialByAdmin " + "listener", e); } } return true; } @Override public boolean doPreDeleteUser(String userName, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostDeleteUser(String s, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreSetUserClaimValue(String s, String s1, String s2, String s3, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostSetUserClaimValue(String s, UserStoreManager userStoreManager) throws UserStoreException { //TODO: need to set last modified time. return true; } @Override public boolean doPreSetUserClaimValues(String userName, Map<String, String> claims, String profileName, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostSetUserClaimValues(String userName, Map<String, String> claims, String profileName, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } String newUserName = claims.get("urn:scim:schemas:core:1.0:userName"); if(newUserName != null && !newUserName.isEmpty()){ userName = newUserName; } //update last-modified-date and proceed if scim enabled. try { Date date = new Date(); String lastModifiedDate = AttributeUtil.formatDateTime(date); userStoreManager.setUserClaimValue( userName, SCIMConstants.META_LAST_MODIFIED_URI, lastModifiedDate, null); } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error in retrieving claim values while provisioning " + "'update user' operation.", e); } return true; } @Override public boolean doPreDeleteUserClaimValues(String s, String[] strings, String s1, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostDeleteUserClaimValues(String s, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreDeleteUserClaimValue(String s, String s1, String s2, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostDeleteUserClaimValue(String s, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreAddRole(String s, String[] strings, org.wso2.carbon.user.api.Permission[] permissions, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostAddRole(String roleName, String[] userList, org.wso2.carbon.user.api.Permission[] permissions, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } try { SCIMGroupHandler scimGroupHandler = new SCIMGroupHandler(userStoreManager.getTenantId()); String domainName = UserCoreUtil.getDomainName(userStoreManager.getRealmConfiguration()); if (domainName == null) { domainName = UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME; } String roleNameWithDomain = UserCoreUtil.addDomainToName(roleName, domainName); // UserCore Util functionality does not append primary roleNameWithDomain = SCIMCommonUtils.getGroupNameWithDomain(roleNameWithDomain); //query role name from identity table try { if (!scimGroupHandler.isGroupExisting(roleNameWithDomain)) { //if no attributes - i.e: group added via mgt console, not via SCIM endpoint //add META scimGroupHandler.addMandatoryAttributes(roleNameWithDomain); } } catch (IdentitySCIMException e) { throw new UserStoreException("Error retrieving group information from SCIM Tables.", e); } return true; } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException(e); } } @Override public boolean doPreDeleteRole(String roleName, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } try { SCIMGroupHandler scimGroupHandler = new SCIMGroupHandler(userStoreManager.getTenantId()); String domainName = UserCoreUtil.getDomainName(userStoreManager.getRealmConfiguration()); if (domainName == null) { domainName = UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME; } String roleNameWithDomain = IdentityUtil.addDomainToName(roleName, domainName); try { //delete group attributes - no need to check existence here, //since it is checked in below method. scimGroupHandler.deleteGroupAttributes(roleNameWithDomain); } catch (IdentitySCIMException e) { throw new UserStoreException("Error retrieving group information from SCIM Tables.", e); } return true; } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException(e); } } @Override public boolean doPostDeleteRole(String roleName, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreUpdateRoleName(String s, String s1, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostUpdateRoleName(String roleName, String newRoleName, UserStoreManager userStoreManager) throws UserStoreException { try { if (!isEnable() || !userStoreManager.isSCIMEnabled()) { return true; } } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException("Error while reading isScimEnabled from userstore manager", e); } try { //TODO:set last update date SCIMGroupHandler scimGroupHandler = new SCIMGroupHandler(userStoreManager.getTenantId()); String domainName = UserCoreUtil.getDomainName(userStoreManager.getRealmConfiguration()); if (domainName == null) { domainName = UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME; } String roleNameWithDomain = UserCoreUtil.addDomainToName(roleName, domainName); String newRoleNameWithDomain = UserCoreUtil.addDomainToName(newRoleName, domainName); try { scimGroupHandler.updateRoleName(roleNameWithDomain, newRoleNameWithDomain); } catch (IdentitySCIMException e) { throw new UserStoreException("Error updating group information in SCIM Tables.", e); } return true; } catch (org.wso2.carbon.user.api.UserStoreException e) { throw new UserStoreException(e); } } @Override public boolean doPreUpdateUserListOfRole(String s, String[] strings, String[] strings1, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPreUpdateRoleListOfUser(String s, String[] strings, String[] strings1, UserStoreManager userStoreManager) throws UserStoreException { return true; } @Override public boolean doPostUpdateRoleListOfUser(String s, String[] strings, String[] strings1, UserStoreManager userStoreManager) throws UserStoreException { //TODO: return true; } public Map<String, String> getSCIMAttributes(String userName, Map<String, String> claimsMap) { Map<String, String> attributes = null; if (claimsMap != null) { attributes = claimsMap; } else { attributes = new HashMap<>(); } if (!attributes.containsKey(SCIMConstants.ID_URI)) { String id = UUID.randomUUID().toString(); attributes.put(SCIMConstants.ID_URI, id); } Date date = new Date(); String createdDate = AttributeUtil.formatDateTime(date); attributes.put(SCIMConstants.META_CREATED_URI, createdDate); attributes.put(SCIMConstants.META_LAST_MODIFIED_URI, createdDate); attributes.put(SCIMConstants.USER_NAME_URI, userName); return attributes; //TODO: add other optional attributes like location etc. } }