/* * Copyright (c) 2014, 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.mgt.services; 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.context.CarbonContext; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.mgt.ChallengeQuestionProcessor; import org.wso2.carbon.identity.mgt.IdentityMgtConfig; import org.wso2.carbon.identity.mgt.IdentityMgtServiceException; import org.wso2.carbon.identity.mgt.constants.IdentityMgtConstants; import org.wso2.carbon.identity.mgt.dto.ChallengeQuestionDTO; import org.wso2.carbon.identity.mgt.dto.UserChallengesDTO; import org.wso2.carbon.identity.mgt.dto.UserChallengesSetDTO; import org.wso2.carbon.identity.mgt.dto.UserDTO; import org.wso2.carbon.identity.mgt.dto.UserIdentityClaimDTO; import org.wso2.carbon.identity.mgt.dto.UserRecoveryDTO; import org.wso2.carbon.identity.mgt.internal.IdentityMgtServiceComponent; import org.wso2.carbon.identity.mgt.util.UserIdentityManagementUtil; import org.wso2.carbon.identity.mgt.util.Utils; import org.wso2.carbon.user.api.AuthorizationManager; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * This is the admin service for the identity management. Some of these * operations are can only be carried out by admins. The other operations are * allowed to all logged in users. * * @author sga */ public class UserIdentityManagementAdminService { private static Log log = LogFactory.getLog(UserIdentityManagementAdminService.class); // --------Operations require Admin permissions ---------// /** * Admin deletes a user from the system. This is an irreversible operation. * * @param userName * @throws IdentityMgtServiceException */ public void deleteUser(String userName) throws IdentityMgtServiceException { try { UserStoreManager userStoreManager = IdentityMgtServiceComponent.getRealmService(). getTenantUserRealm(CarbonContext.getThreadLocalCarbonContext().getTenantId()).getUserStoreManager(); userStoreManager.deleteUser(userName); log.info("Deleted user: " + userName); } catch (UserStoreException e) { String errorMessage = "Error occured while deleting user : " + userName; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage); } } /** * Admin locks the user account. Only the admin can unlock the account using * the {@literal unlockUserAccount} method. * * @param userName * @throws IdentityMgtServiceException */ public void lockUserAccount(String userName) throws IdentityMgtServiceException { try { UserStoreManager userStoreManager = getUserStore(userName); String userNameWithoutDomain = UserCoreUtil.removeDomainFromName(userName); UserIdentityManagementUtil.lockUserAccount(userNameWithoutDomain, userStoreManager); log.info("User account locked: " + userName); } catch (UserStoreException|IdentityException e) { log.error("Error occurred while trying to lock the account " + userName, e); throw new IdentityMgtServiceException("Error occurred while trying to lock the account " + userName, e); } } /** * Admin unlocks the user account. * * @param userName * @throws IdentityMgtServiceException */ public void unlockUserAccount(String userName, String notificationType) throws IdentityMgtServiceException { try { UserStoreManager userStoreManager = getUserStore(userName); String userNameWithoutDomain = UserCoreUtil.removeDomainFromName(userName); UserIdentityManagementUtil.unlockUserAccount(userNameWithoutDomain, userStoreManager); int tenantID = userStoreManager.getTenantId(); String tenantDomain = IdentityMgtServiceComponent.getRealmService().getTenantManager().getDomain(tenantID); boolean isNotificationSending = IdentityMgtConfig.getInstance().isNotificationSending(); if (notificationType != null && isNotificationSending) { UserRecoveryDTO dto; if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { dto = new UserRecoveryDTO(userName); } else { UserDTO userDTO = new UserDTO(UserCoreUtil.addTenantDomainToEntry(userName, tenantDomain)); userDTO.setTenantId(tenantID); dto = new UserRecoveryDTO(userDTO); } dto.setNotification(IdentityMgtConstants.Notification.ACCOUNT_UNLOCK); dto.setNotificationType(notificationType); IdentityMgtServiceComponent.getRecoveryProcessor().recoverWithNotification(dto); } log.info("Account unlocked for: " + userName); } catch (UserStoreException|IdentityException e) { String message = "Error occurred while unlocking account for: " + userName; log.error(message, e); throw new IdentityMgtServiceException(message, e); } } /** * Admin resets the password of the user. * * @param userName * @param newPassword * @throws IdentityMgtServiceException */ public void resetUserPassword(String userName, String newPassword) throws IdentityMgtServiceException { try { UserStoreManager userStoreManager = getUserStore(userName); String userNameWithoutDomain = UserCoreUtil.removeDomainFromName(userName); userStoreManager.updateCredentialByAdmin(userNameWithoutDomain, newPassword); log.info("User password reset for: " + userName); } catch (UserStoreException e) { String message = "Error occurred while resetting password for: " + userName; log.error(message, e); throw new IdentityMgtServiceException(message, e); } } /** * get challenges of user * * @param userName bean class that contains user and tenant Information * @return array of challenges if null, return empty array * @throws org.wso2.carbon.identity.mgt.IdentityMgtServiceException if fails */ public UserChallengesDTO[] getChallengeQuestionsOfUser(String userName) throws IdentityMgtServiceException { int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); String loggedInName = CarbonContext.getThreadLocalCarbonContext().getUsername(); if(userName != null && !userName.equals(loggedInName)){ AuthorizationManager authzManager = null; try { authzManager = IdentityMgtServiceComponent.getRealmService().getTenantUserRealm(tenantId). getAuthorizationManager(); } catch (UserStoreException e) { throw new IdentityMgtServiceException("Error occurred while retrieving AuthorizationManager for tenant " + tenantDomain, e); } boolean isAuthorized = false; try { isAuthorized = authzManager.isUserAuthorized(loggedInName, "/permission/admin/configure/security", CarbonConstants.UI_PERMISSION_ACTION); } catch (UserStoreException e) { throw new IdentityMgtServiceException("Error occurred while checking access level for " + "user " + userName + " in tenant " + tenantDomain, e); } if(!isAuthorized){ throw new IdentityMgtServiceException("Unauthorized access!! Possible violation of confidentiality. " + "User " + loggedInName + " trying to get challenge questions for user " + userName); } } else if (userName == null){ userName = loggedInName; } ChallengeQuestionProcessor processor = IdentityMgtServiceComponent. getRecoveryProcessor().getQuestionProcessor(); return processor.getChallengeQuestionsOfUser(userName, tenantId, true); } /** * get all promoted user challenges * * @return array of user challenges * @throws IdentityMgtServiceException if fails */ public UserChallengesSetDTO[] getAllPromotedUserChallenge() throws IdentityMgtServiceException { ChallengeQuestionProcessor processor = IdentityMgtServiceComponent. getRecoveryProcessor().getQuestionProcessor(); List<UserChallengesSetDTO> challengeQuestionSetDTOs = new ArrayList<UserChallengesSetDTO>(); List<ChallengeQuestionDTO> questionDTOs = null; try { questionDTOs = processor.getAllChallengeQuestions(); } catch (IdentityException e) { log.error("Error while loading user challenges", e); throw new IdentityMgtServiceException("Error while loading user challenges"); } Map<String, List<UserChallengesDTO>> listMap = new HashMap<String, List<UserChallengesDTO>>(); for (ChallengeQuestionDTO dto : questionDTOs) { List<UserChallengesDTO> dtoList = listMap.get(dto.getQuestionSetId()); if (dtoList == null) { dtoList = new ArrayList<UserChallengesDTO>(); } UserChallengesDTO userChallengesDTO = new UserChallengesDTO(); userChallengesDTO.setId(dto.getQuestionSetId()); userChallengesDTO.setQuestion(dto.getQuestion()); userChallengesDTO.setOrder(dto.getOrder()); dtoList.add(userChallengesDTO); listMap.put(dto.getQuestionSetId(), dtoList); } for (Map.Entry<String, List<UserChallengesDTO>> listEntry : listMap.entrySet()) { UserChallengesSetDTO dto = new UserChallengesSetDTO(); dto.setId(listEntry.getKey()); List<UserChallengesDTO> dtoList = listEntry.getValue(); dto.setChallengesDTOs(dtoList.toArray(new UserChallengesDTO[dtoList.size()])); challengeQuestionSetDTOs.add(dto); } return challengeQuestionSetDTOs.toArray(new UserChallengesSetDTO[challengeQuestionSetDTOs.size()]); } /** * get all challenge questions * * @return array of questions * @throws IdentityMgtServiceException if fails */ public ChallengeQuestionDTO[] getAllChallengeQuestions() throws IdentityMgtServiceException { ChallengeQuestionProcessor processor = IdentityMgtServiceComponent. getRecoveryProcessor().getQuestionProcessor(); List<ChallengeQuestionDTO> questionDTOs = null; try { questionDTOs = processor.getAllChallengeQuestions(); } catch (IdentityException e) { String errorMessage = "Error while loading user challenge questions"; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage); } return questionDTOs.toArray(new ChallengeQuestionDTO[questionDTOs.size()]); } /** * set all challenge questions * * @param challengeQuestionDTOs array of questions * @throws IdentityMgtServiceException if fails */ public void setChallengeQuestions(ChallengeQuestionDTO[] challengeQuestionDTOs) throws IdentityMgtServiceException { ChallengeQuestionProcessor processor = IdentityMgtServiceComponent. getRecoveryProcessor().getQuestionProcessor(); try { processor.setChallengeQuestions(challengeQuestionDTOs); } catch (IdentityException e) { log.error("Error while persisting user challenges", e); throw new IdentityMgtServiceException("Error while persisting user challenges"); } } /** * set challenges of user * * @param userName bean class that contains user and tenant Information * @throws IdentityMgtServiceException if fails */ public void setChallengeQuestionsOfUser(String userName, UserChallengesDTO[] challengesDTOs) throws IdentityMgtServiceException { if (challengesDTOs == null || challengesDTOs.length < 1) { log.error("no challenges provided by user"); throw new IdentityMgtServiceException("no challenges provided by user"); } int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); String loggedInName = CarbonContext.getThreadLocalCarbonContext().getUsername(); if(userName != null && !userName.equals(loggedInName)){ AuthorizationManager authzManager = null; try { authzManager = IdentityMgtServiceComponent.getRealmService().getTenantUserRealm(tenantId). getAuthorizationManager(); } catch (UserStoreException e) { throw new IdentityMgtServiceException("Error occurred while retrieving AuthorizationManager for tenant " + tenantDomain, e); } boolean isAuthorized = false; try { isAuthorized = authzManager.isUserAuthorized(loggedInName, "/permission/admin/configure/security", CarbonConstants.UI_PERMISSION_ACTION); } catch (UserStoreException e) { throw new IdentityMgtServiceException("Error occurred while checking access level for " + "user " + userName + " in tenant " + tenantDomain, e); } if(!isAuthorized){ throw new IdentityMgtServiceException("Unauthorized access!! Possible elevation of privilege attack. " + "User " + loggedInName + " trying to change challenge questions for user " + userName); } } else if (userName == null){ userName = loggedInName; } validateSecurityQuestionDuplicate(challengesDTOs); ChallengeQuestionProcessor processor = IdentityMgtServiceComponent. getRecoveryProcessor().getQuestionProcessor(); try { List<ChallengeQuestionDTO> challengeQuestionDTOs = processor.getAllChallengeQuestions(); for (UserChallengesDTO userChallengesDTO : challengesDTOs){ boolean found = false ; for (ChallengeQuestionDTO challengeQuestionDTO :challengeQuestionDTOs ){ if(challengeQuestionDTO.getQuestion().equals(userChallengesDTO.getQuestion()) && challengeQuestionDTO.getQuestionSetId().equals(userChallengesDTO.getId())){ found = true ; break ; } } if(!found){ String errMsg = "Error while persisting user challenges for user : " + userName + ", because these user challengers are not registered with the tenant" ; log.error(errMsg); throw new IdentityMgtServiceException(errMsg); } } processor.setChallengesOfUser(userName, tenantId, challengesDTOs); } catch (IdentityException e) { String errorMessage = "Error while persisting user challenges for user : " + userName; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage); } } /** * User updates/add account recovery data such as the email address or the * phone number etc. * * @param userIdentityClaims * @throws IdentityMgtServiceException */ public void updateUserIdentityClaims(UserIdentityClaimDTO[] userIdentityClaims) throws IdentityMgtServiceException { String userName = CarbonContext.getThreadLocalCarbonContext().getUsername(); try { UserStoreManager userStoreManager = IdentityMgtServiceComponent.getRealmService() .getTenantUserRealm(CarbonContext.getThreadLocalCarbonContext().getTenantId()) .getUserStoreManager(); Map<String, String> claims = new HashMap<String, String>(); for (UserIdentityClaimDTO dto : userIdentityClaims) { if (dto.getClaimUri().contains(UserCoreConstants.ClaimTypeURIs.IDENTITY_CLAIM_URI)) { log.warn("WARNING! User " + userName + " tried to alter " + dto.getClaimUri()); throw IdentityException.error("Updates to the claim " + dto.getClaimUri() + " are not allowed"); } claims.put(dto.getClaimUri(), dto.getClaimValue()); } userStoreManager.setUserClaimValues(userName, claims, null); } catch (UserStoreException|IdentityException e) { String errorMessage = "Error while updating identity recovery data for : " + userName; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage, e); } } /** * Returns all user claims which can be used in the identity recovery * process * such as the email address, telephone number etc * * @return * @throws IdentityMgtServiceException */ public UserIdentityClaimDTO[] getAllUserIdentityClaims() throws IdentityMgtServiceException { String userName = CarbonContext.getThreadLocalCarbonContext().getUsername(); return UserIdentityManagementUtil.getAllUserIdentityClaims(userName); } /** * User change the password of the user. * * @param newPassword * @throws IdentityMgtServiceException */ public void changeUserPassword(String newPassword, String oldPassword) throws IdentityMgtServiceException { String userName = CarbonContext.getThreadLocalCarbonContext().getUsername(); try { UserStoreManager userStoreManager = getUserStore(userName); userName = UserCoreUtil.removeDomainFromName(userName); userStoreManager.updateCredential(userName, newPassword, oldPassword); log.info("Password changed for: " + userName); } catch (UserStoreException e) { String message = "Error while resetting the password for: " + userName; log.error(message, e); throw new IdentityMgtServiceException(message, e); } } /** * This method is used to check the user's user store is read only. * * @param userName * @param tenantDomain * @return * @throws IdentityMgtServiceException */ public boolean isReadOnlyUserStore(String userName, String tenantDomain) throws IdentityMgtServiceException { boolean isReadOnly = false; org.wso2.carbon.user.core.UserStoreManager userStoreManager = null; if (StringUtils.isEmpty(tenantDomain)) { tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; } RealmService realmService = IdentityMgtServiceComponent.getRealmService(); int tenantId; try { tenantId = Utils.getTenantId(tenantDomain); if (realmService.getTenantUserRealm(tenantId) != null) { userStoreManager = (org.wso2.carbon.user.core.UserStoreManager) getUserStore(userName); } } catch (Exception e) { String msg = "Error retrieving the user store manager for the tenant"; log.error(msg, e); throw new IdentityMgtServiceException(msg); } try { if (userStoreManager != null && userStoreManager.isReadOnly()) { isReadOnly = true; } else isReadOnly = false; } catch (org.wso2.carbon.user.core.UserStoreException e) { String errorMessage = "Error while retrieving user store manager"; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage); } return isReadOnly; } private UserStoreManager getUserStore(String userName) throws UserStoreException { UserStoreManager userStoreManager = IdentityMgtServiceComponent.getRealmService(). getTenantUserRealm(CarbonContext.getThreadLocalCarbonContext().getTenantId()).getUserStoreManager(); if (userName != null && userName.contains(UserCoreConstants.DOMAIN_SEPARATOR)) { String userStoreDomain = getUserStoreDomainName(userName); return ((AbstractUserStoreManager) userStoreManager) .getSecondaryUserStoreManager(userStoreDomain); } else { return userStoreManager; } } private String getUserStoreDomainName(String userName) { String userNameWithoutDomain = userName; int index; if ((index = userName.indexOf(CarbonConstants.DOMAIN_SEPARATOR)) >= 0) { // remove domain name if exist userNameWithoutDomain = userName.substring(0, index); } return userNameWithoutDomain; } private void validateSecurityQuestionDuplicate(UserChallengesDTO[] challengesDTOs) throws IdentityMgtServiceException { Set<String> tmpMap = new HashSet<String>(); for(int i = 0; i < challengesDTOs.length ; i++) { UserChallengesDTO userChallengesDTO = challengesDTOs[i]; if(tmpMap.contains(userChallengesDTO.getId())){ String errMsg = "Error while validating user challenges, because these can't be more than one security challenges for one claim uri" ; log.error(errMsg); throw new IdentityMgtServiceException(errMsg); } tmpMap.add(userChallengesDTO.getId()); } } }