/* * 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.captcha.mgt.beans.CaptchaInfoBean; import org.wso2.carbon.captcha.mgt.util.CaptchaUtil; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.base.IdentityConstants; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.core.IdentityClaimManager; import org.wso2.carbon.identity.core.model.IdentityEventListenerConfig; import org.wso2.carbon.identity.core.util.IdentityCoreConstants; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.mgt.IdentityMgtEventListener; 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.RecoveryProcessor; import org.wso2.carbon.identity.mgt.beans.VerificationBean; import org.wso2.carbon.identity.mgt.constants.IdentityMgtConstants; import org.wso2.carbon.identity.mgt.dto.ChallengeQuestionDTO; import org.wso2.carbon.identity.mgt.dto.ChallengeQuestionIdsDTO; import org.wso2.carbon.identity.mgt.dto.NotificationDataDTO; import org.wso2.carbon.identity.mgt.dto.UserChallengesDTO; 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.UserStoreException; import org.wso2.carbon.user.core.Permission; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserRealm; import org.wso2.carbon.user.core.claim.Claim; import org.wso2.carbon.user.core.listener.UserOperationEventListener; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.tenant.TenantManager; import org.wso2.carbon.user.mgt.UserMgtConstants; import org.wso2.carbon.user.core.util.UserCoreUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This service provides the services needed to recover user password and user * account information. */ public class UserInformationRecoveryService { Log log = LogFactory.getLog(UserInformationRecoveryService.class); public CaptchaInfoBean getCaptcha() throws IdentityMgtServiceException { if (log.isDebugEnabled()) { log.debug("User get captcha image request received"); } try { CaptchaUtil.cleanOldCaptchas(); CaptchaInfoBean bean = CaptchaUtil.generateCaptchaImage(); if (log.isDebugEnabled()) { log.debug("Captcha stored: " + bean.getImagePath()); log.debug("Captcha generated successfully"); } return bean; } catch (Exception e) { String errorMessage = "Error while generating captcha"; log.error(errorMessage, e); throw new IdentityMgtServiceException(errorMessage); } } public VerificationBean verifyUser(String username, CaptchaInfoBean captcha) throws IdentityMgtServiceException { UserDTO userDTO; VerificationBean bean; if (log.isDebugEnabled()) { log.debug("User verification request received with username : " + username); } if (IdentityMgtConfig.getInstance().isCaptchaVerificationInternallyManaged()) { try { CaptchaUtil.processCaptchaInfoBean(captcha); } catch (Exception e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CAPTCHA + " Error while validating captcha", e); return bean; } } try { userDTO = Utils.processUserId(username); } catch (IdentityException e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " invalid user : " + username, e); return bean; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); bean = processor.verifyUserForRecovery(1, userDTO); if (bean.getError() != null) { if (bean.getError().contains(VerificationBean.ERROR_CODE_INVALID_USER)) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " User does not exist : " + username, null); } else { bean = handleError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error verifying user : " + username, null); } } return bean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } } public VerificationBean sendRecoveryNotification(String username, String key, String notificationType) throws IdentityMgtServiceException { UserDTO userDTO = null; VerificationBean bean = null; if (log.isDebugEnabled()) { log.debug("User recovery notification sending request received with username : " + username + " notification" + " type :" + notificationType); } try { userDTO = Utils.processUserId(username); } catch (IdentityException e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " invalid user : " + username, e); return bean; } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } bean = processor.verifyConfirmationCode(1, userDTO.getUserId(), key); if (!bean.isVerified()) { log.error("Invalid user is trying to recover the password with username : " + username); bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " Invalid user is trying to recover the password with username : " + username, null); return bean; } } catch (IdentityException e1) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e1, username); if (bean.getError() == null) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CODE + " Invalid confirmation code for user : " + username, e1); } return bean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } UserRecoveryDTO dto = new UserRecoveryDTO(userDTO); dto.setNotification(IdentityMgtConstants.Notification.PASSWORD_RESET_RECOVERY); dto.setNotificationType(notificationType); NotificationDataDTO dataDTO = null; try { if (log.isDebugEnabled()) { log.debug("Initiating the notification sending process"); } if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } dataDTO = processor.recoverWithNotification(dto); // Send email data only if not internally managed. if (!(IdentityMgtConfig.getInstance().isNotificationInternallyManaged())) { bean.setNotificationData(dataDTO); } } catch (IdentityException e) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e, username); if (bean.getError() == null) { bean = handleError(VerificationBean.ERROR_CODE_RECOVERY_NOTIFICATION_FAILURE + ": " + VerificationBean. ERROR_CODE_UNEXPECTED + " Error when sending recovery message for " + "user: " + username, e); } return bean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return bean; } /** * This method is used to verify the confirmation code sent to user is * correct and validates. Before calling this method it needs to supply a * Captcha and should call getCaptcha(). * * @param username - username of whom the password needs to be recovered. * @param code - confirmation code sent to user by notification. * @param captcha - generated captcha with answer for this communication. * @return - VerificationBean with new code to be used in updatePassword(). * @throws IdentityMgtServiceException */ public VerificationBean verifyConfirmationCode(String username, String code, CaptchaInfoBean captcha) throws IdentityMgtServiceException { UserDTO userDTO; VerificationBean bean = new VerificationBean(); if (log.isDebugEnabled()) { log.debug("User confirmation code verification request received with username :" + username); } if (IdentityMgtConfig.getInstance().isCaptchaVerificationInternallyManaged()) { try { CaptchaUtil.processCaptchaInfoBean(captcha); } catch (Exception e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CODE + " Error while validating captcha for user : " + username, e); return bean; } } try { userDTO = Utils.processUserId(username); } catch (IdentityException e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " invalid user : " + username, e); return bean; } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } try { bean = processor.verifyConfirmationCode(2, userDTO.getUserId(), code); if (bean.isVerified()) { bean = processor.updateConfirmationCode(3, userDTO.getUserId(), userDTO.getTenantId()); if (log.isDebugEnabled()) { log.debug("User confirmation code verification successful for user: " + username); } } else { bean.setVerified(false); bean.setKey(""); log.error(bean.getError()); } } catch (IdentityException e) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e, username); if (bean.getError() == null) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CODE + " Error verifying confirmation code for " + "user : " + username, e); } return bean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return bean; } /** * This method is used to update the password in the system for password * recovery process. Before calling this method caller needs to call * verifyConfirmationCode and get the newly generated confirmation code. * * @param username - username * @param confirmationCode - newly generated confirmation code * @param newPassword - new password * @return - VerificationBean with operation status true or false. * @throws IdentityMgtServiceException */ public VerificationBean updatePassword(String username, String confirmationCode, String newPassword) throws IdentityMgtServiceException { RecoveryProcessor recoveryProcessor = IdentityMgtServiceComponent.getRecoveryProcessor(); VerificationBean bean = null; if (log.isDebugEnabled()) { log.debug("User update password request received with username: " + username); } try { UserDTO userDTO = Utils.processUserId(username); if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } TenantManager tenantManager = IdentityMgtServiceComponent.getRealmService().getTenantManager(); int tenantId = 0; try { tenantId = tenantManager.getTenantId(userDTO.getTenantDomain()); } catch (UserStoreException e) { log.warn("No Tenant id for tenant domain " + userDTO.getTenantDomain()); return handleError(VerificationBean.ERROR_CODE_INVALID_TENANT + " No Tenant id for tenant domain : " + userDTO.getTenantDomain(), e); } if (recoveryProcessor.verifyConfirmationCode(30, userDTO.getUserId(), confirmationCode).isVerified()) { Utils.updatePassword(userDTO.getUserId(), tenantId, newPassword); log.info("Credential is updated for user : " + userDTO.getUserId() + " and tenant domain : " + userDTO.getTenantDomain()); IdentityMgtConfig.getInstance().getRecoveryDataStore().invalidate(userDTO.getUserId(), tenantId); bean = new VerificationBean(true); } else if (recoveryProcessor.verifyConfirmationCode(3, userDTO.getUserId(), confirmationCode).isVerified()) { Utils.updatePassword(userDTO.getUserId(), tenantId, newPassword); log.info("Credential is updated for user : " + userDTO.getUserId() + " and tenant domain : " + userDTO.getTenantDomain()); IdentityMgtConfig.getInstance().getRecoveryDataStore().invalidate(userDTO.getUserId(), tenantId); bean = new VerificationBean(true); } else { String msg = "Invalid user tried to update credential with user Id : " + userDTO.getUserId() + " and tenant domain : " + userDTO.getTenantDomain(); bean = new VerificationBean(VerificationBean.ERROR_CODE_INVALID_USER + " " + msg); bean.setVerified(false); log.error(msg); } } catch (IdentityException e) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e, username); if (bean.getError() == null) { bean = handleError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error while updating credential " + "for user: " + username, e); } return bean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return bean; } public ChallengeQuestionIdsDTO getUserChallengeQuestionIds(String username, String confirmation) throws IdentityMgtServiceException { UserDTO userDTO = null; ChallengeQuestionIdsDTO idsDTO = new ChallengeQuestionIdsDTO(); if (log.isDebugEnabled()) { log.debug("User challenge questions id request received with username: " + username); } try { userDTO = Utils.processUserId(username); } catch (IdentityException e) { idsDTO = handleChallengeIdError(VerificationBean.ERROR_CODE_INVALID_USER + " Error validating user : " + username, e); return idsDTO; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); VerificationBean bean = null; try { bean = processor.verifyConfirmationCode(1, userDTO.getUserId(), confirmation); if (bean.isVerified()) { bean = processor.updateConfirmationCode(20, userDTO.getUserId(), userDTO.getTenantId()); } else { bean.setVerified(false); } } catch (IdentityException e1) { idsDTO = UserIdentityManagementUtil.getCustomErrorMessagesForChallengeQuestionIds(e1, username); if (idsDTO == null) { idsDTO = handleChallengeIdError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error when validating " + "code", e1); } return idsDTO; } if (bean.isVerified()) { try { idsDTO = processor.getQuestionProcessor().getUserChallengeQuestionIds(userDTO.getUserId(), userDTO.getTenantId()); idsDTO.setKey(bean.getKey()); if (log.isDebugEnabled()) { log.debug("User challenge question response successful for user: " + username); } } catch (Exception e) { idsDTO = handleChallengeIdError(VerificationBean.ERROR_CODE_CHALLENGE_QUESTION_NOT_FOUND + " Error when getting user challenge questions for user : " + username, e); return idsDTO; } } else { String msg = "Verification failed for user. Error : " + bean.getError(); log.error(msg); idsDTO.setError(VerificationBean.ERROR_CODE_UNEXPECTED + " " + msg); idsDTO.setKey(""); } }finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return idsDTO; } /** * To get the challenge question for the user. * * @param userName * @param confirmation * @param questionId - Question id returned from the getUserChanllegneQuestionIds * method. * @return Populated question bean with the question details and the key. * @throws IdentityMgtServiceException */ public UserChallengesDTO getUserChallengeQuestion(String userName, String confirmation, String questionId) throws IdentityMgtServiceException { UserDTO userDTO = null; UserChallengesDTO userChallengesDTO = new UserChallengesDTO(); if (log.isDebugEnabled()) { log.debug("User challenge question request received with username :" + userName); } try { userDTO = Utils.processUserId(userName); } catch (IdentityException e) { return handleChallengesError(VerificationBean.ERROR_CODE_INVALID_USER + " Error validating user : " + userName, null); } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); VerificationBean bean; try { bean = processor.verifyConfirmationCode(20, userDTO.getUserId(), confirmation); if (bean.isVerified()) { bean = processor.updateConfirmationCode(40, userDTO.getUserId(), userDTO.getTenantId()); } else if (processor.verifyConfirmationCode(30, userDTO.getUserId(), confirmation).isVerified()) { bean = processor.updateConfirmationCode(40, userDTO.getUserId(), userDTO.getTenantId()); } else { bean.setVerified(false); } } catch (IdentityException e) { userChallengesDTO = UserIdentityManagementUtil.getCustomErrorMessagesForChallengQuestions(e, userName); if (userChallengesDTO == null) { userChallengesDTO = handleChallengesError(VerificationBean.ERROR_CODE_INVALID_CODE + " Invalid confirmation code for user : " + userName, e); } return userChallengesDTO; } if (bean.isVerified()) { userChallengesDTO = processor.getQuestionProcessor().getUserChallengeQuestion( userDTO.getUserId(), userDTO.getTenantId(), questionId); userChallengesDTO.setKey(bean.getKey()); userChallengesDTO.setVerfied(true); if (log.isDebugEnabled()) { log.debug("User challenge question retrieved successfully"); } } else { if (log.isDebugEnabled()) { log.debug("Verification failed for user. Error : " + bean.getError()); } userChallengesDTO.setError(VerificationBean.ERROR_CODE_INVALID_USER + " " + bean.getError()); } } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return userChallengesDTO; } /** * This method is to verify the user supplied answer for the challenge * question. * * @param userName * @param confirmation * @param questionId * @param answer * @return status and key details about the operation status. * @throws IdentityMgtServiceException */ public VerificationBean verifyUserChallengeAnswer(String userName, String confirmation, String questionId, String answer) throws IdentityMgtServiceException { VerificationBean bean = new VerificationBean(); bean.setVerified(false); if (log.isDebugEnabled()) { log.debug("User challenge answer request received with username :" + userName); } if (questionId == null || answer == null) { String error = "No challenge question id provided for verification"; bean.setError(error); if (log.isDebugEnabled()) { log.debug(error); } return bean; } UserDTO userDTO = null; try { userDTO = Utils.processUserId(userName); } catch (IdentityException e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " Error verifying user: " + userName, e); return bean; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } RecoveryProcessor recoveryProcessor = IdentityMgtServiceComponent.getRecoveryProcessor(); try { bean = recoveryProcessor.verifyConfirmationCode(40, userDTO.getUserId(), confirmation); if (bean.isVerified()) { bean = recoveryProcessor.updateConfirmationCode(30, userDTO.getUserId(), userDTO.getTenantId()); } else { bean.setVerified(false); } } catch (IdentityException e) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e, userName); if (bean == null) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CODE + " " + " Error verifying confirmation code for user : " + userName, e); } return bean; } ChallengeQuestionProcessor processor = recoveryProcessor.getQuestionProcessor(); UserChallengesDTO userChallengesDTO = new UserChallengesDTO(); userChallengesDTO.setId(questionId); userChallengesDTO.setAnswer(answer); boolean verification = processor.verifyUserChallengeAnswer(userDTO.getUserId(), userDTO.getTenantId(), userChallengesDTO); if (verification) { bean.setError(""); bean.setUserId(userName); if (log.isDebugEnabled()) { log.debug("User answer verification successful for user: " + userName); } } else { bean.setError("Challenge answer verification failed for user : " + userName); bean.setVerified(false); bean.setKey(""); // clear the key to avoid returning to caller. log.error(bean.getError()); } } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return bean; } /** * 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) { log.error("Error while loading user challenges", e); throw new IdentityMgtServiceException("Error while loading user challenges"); } return questionDTOs.toArray(new ChallengeQuestionDTO[questionDTOs.size()]); } /** * This returns the user supported claims. * * @param dialect * @return * @throws IdentityException */ public UserIdentityClaimDTO[] getUserIdentitySupportedClaims(String dialect) throws IdentityException { IdentityClaimManager claimManager = null; Claim[] claims = null; UserRealm realm = null; claimManager = IdentityClaimManager.getInstance(); realm = IdentityTenantUtil.getRealm(null, null); claims = claimManager.getAllSupportedClaims(dialect, realm); if (claims == null || claims.length == 0) { log.warn("Could not find any matching claims for requested dialect : " + dialect); return new UserIdentityClaimDTO[0]; } List<UserIdentityClaimDTO> claimList = new ArrayList<UserIdentityClaimDTO>(); for (int i = 0; i < claims.length; i++) { if (claims[i].getDisplayTag() != null && !IdentityConstants.PPID_DISPLAY_VALUE.equals(claims[i].getDisplayTag())) { if (UserCoreConstants.ClaimTypeURIs.ACCOUNT_STATUS.equals(claims[i].getClaimUri())) { continue; } if (claims[i].isSupportedByDefault() && (!claims[i].isReadOnly())) { UserIdentityClaimDTO claimDto = new UserIdentityClaimDTO(); claimDto.setClaimUri(claims[i].getClaimUri()); claimDto.setClaimValue(claims[i].getValue()); claimList.add(claimDto); } } } return claimList.toArray(new UserIdentityClaimDTO[claimList.size()]); } /** * Verifies the user against the provided claims and captcha information. * * @param claims * @param captcha * @param tenantDomain * @return * @throws IdentityMgtServiceException */ public VerificationBean verifyAccount(UserIdentityClaimDTO[] claims, CaptchaInfoBean captcha, String tenantDomain) throws IdentityMgtServiceException { VerificationBean vBean = new VerificationBean(); if (IdentityMgtConfig.getInstance().isCaptchaVerificationInternallyManaged()) { try { CaptchaUtil.processCaptchaInfoBean(captcha); } catch (Exception e) { vBean = handleError(VerificationBean.ERROR_CODE_INVALID_CAPTCHA + " Error processing captcha", e); return vBean; } } if (!IdentityMgtConfig.getInstance().isSaasEnabled()) { String loggedInTenant = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); if (tenantDomain != null && !tenantDomain.isEmpty() && !loggedInTenant.equals(tenantDomain)) { String msg = "Trying to verify account unauthorized tenant space"; log.error(msg); throw new IdentityMgtServiceException(msg); } if (tenantDomain == null || tenantDomain.isEmpty()) { tenantDomain = loggedInTenant; } } try { int tenantId = Utils.getTenantId(tenantDomain); String userName = UserIdentityManagementUtil.getUsernameByClaims(claims, tenantId); if (userName != null) { UserDTO userDTO = new UserDTO(UserCoreUtil.addTenantDomainToEntry(userName, tenantDomain)); userDTO.setTenantId(tenantId); UserRecoveryDTO dto = new UserRecoveryDTO(userDTO); dto.setNotification(IdentityMgtConstants.Notification.ACCOUNT_ID_RECOVERY); dto.setNotificationType("EMAIL"); RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); NotificationDataDTO notificationDto = processor.notifyWithEmail(dto); vBean.setVerified(notificationDto.isNotificationSent()); // Send email data only if not internally managed. if (!(IdentityMgtConfig.getInstance().isNotificationInternallyManaged())) { vBean.setNotificationData(notificationDto); } } else { vBean.setError("User not found"); vBean.setVerified(false); } } catch (Exception e) { vBean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " Error verifying user account", e); return vBean; } return vBean; } /** * This method is used to register an user in the system. The account will be locked if the * Authentication.Policy.Account.Lock.On.Creation is set to true. Else user will be able to * login after registration. * * @param userName * @param password * @param claims * @param profileName * @param tenantDomain * @return * @throws IdentityMgtServiceException */ public VerificationBean registerUser(String userName, String password, UserIdentityClaimDTO[] claims, String profileName, String tenantDomain) throws IdentityMgtServiceException { VerificationBean vBean = new VerificationBean(); org.wso2.carbon.user.core.UserStoreManager userStoreManager = null; Permission permission = null; if (!IdentityMgtConfig.getInstance().isSaasEnabled()) { String loggedInTenant = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); if (tenantDomain != null && !tenantDomain.isEmpty() && !loggedInTenant.equals(tenantDomain)) { String msg = "Trying to create users in unauthorized tenant space"; log.error(msg); throw new IdentityMgtServiceException(msg); } if (tenantDomain == null || tenantDomain.isEmpty()) { tenantDomain = loggedInTenant; } } RealmService realmService = IdentityMgtServiceComponent.getRealmService(); int tenantId; try { tenantId = Utils.getTenantId(tenantDomain); if (realmService.getTenantUserRealm(tenantId) != null) { userStoreManager = (org.wso2.carbon.user.core.UserStoreManager) realmService .getTenantUserRealm(tenantId).getUserStoreManager(); } } catch (Exception e) { vBean = handleError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error retrieving the user store manager for the tenant", e); return vBean; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(tenantId); carbonContext.setTenantDomain(tenantDomain); } if (userStoreManager == null) { vBean = new VerificationBean(); vBean.setVerified(false); vBean.setError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error retrieving the user store manager for the tenant"); return vBean; } Map<String, String> claimsMap = new HashMap<String, String>(); for (UserIdentityClaimDTO userIdentityClaimDTO : claims) { claimsMap.put(userIdentityClaimDTO.getClaimUri(), userIdentityClaimDTO.getClaimValue()); } userStoreManager.addUser(userName, password, null, claimsMap, profileName); String identityRoleName = UserCoreConstants.INTERNAL_DOMAIN + CarbonConstants.DOMAIN_SEPARATOR + IdentityConstants.IDENTITY_DEFAULT_ROLE; if (!userStoreManager.isExistingRole(identityRoleName, false)) { permission = new Permission("/permission/admin/login", UserMgtConstants.EXECUTE_ACTION); userStoreManager.addRole(identityRoleName, new String[]{userName}, new Permission[]{permission}, false); } else { userStoreManager.updateUserListOfRole(identityRoleName, new String[]{}, new String[]{userName}); } IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty (UserOperationEventListener.class.getName(), IdentityMgtEventListener.class.getName()); boolean isListenerEnable = true; if (identityEventListenerConfig != null) { if (StringUtils.isNotBlank(identityEventListenerConfig.getEnable())) { isListenerEnable = Boolean.parseBoolean(identityEventListenerConfig.getEnable()); } } IdentityMgtConfig config = IdentityMgtConfig.getInstance(); if (isListenerEnable && config.isAuthPolicyAccountLockOnCreation()) { UserDTO userDTO = new UserDTO(UserCoreUtil.addTenantDomainToEntry(userName, tenantDomain)); userDTO.setTenantId(tenantId); UserRecoveryDTO dto = new UserRecoveryDTO(userDTO); dto.setNotification(IdentityMgtConstants.Notification.ACCOUNT_CONFORM); dto.setNotificationType("EMAIL"); RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); vBean = processor.updateConfirmationCode(1, userName, tenantId); dto.setConfirmationCode(vBean.getKey()); NotificationDataDTO notificationDto = processor.notifyWithEmail(dto); vBean.setVerified(notificationDto.isNotificationSent()); // Send email data only if not internally managed. if (!(IdentityMgtConfig.getInstance().isNotificationInternallyManaged())) { vBean.setNotificationData(notificationDto); } } else { vBean.setVerified(true); } } catch (UserStoreException | IdentityException e) { vBean = UserIdentityManagementUtil.getCustomErrorMessagesWhenRegistering(e, userName); //Rollback if user exists try { if (!e.getMessage().contains(IdentityCoreConstants.EXISTING_USER) && userStoreManager.isExistingUser(userName)) { userStoreManager.deleteUser(userName); } } catch (UserStoreException e1) { vBean = UserIdentityManagementUtil.getCustomErrorMessagesWhenRegistering(e1, userName); } return vBean; } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return vBean; } /** * This method is used to resend selef sign up confiration code when user is not recieved email properly * * @param userName * @param code * @param profileName * @param tenantDomain * @return * @throws IdentityMgtServiceException */ public VerificationBean resendSignUpConfiramtionCode(String userName, String code, String profileName, String tenantDomain) throws IdentityMgtServiceException { VerificationBean vBean = new VerificationBean(); RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); if (!IdentityMgtConfig.getInstance().isSaasEnabled()) { String loggedInTenant = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); if (tenantDomain != null && !tenantDomain.isEmpty() && !loggedInTenant.equals(tenantDomain)) { String msg = "Trying to resend self sign up code in unauthorized tenant space"; log.error(msg); throw new IdentityMgtServiceException(msg); } if (tenantDomain == null || tenantDomain.isEmpty()) { tenantDomain = loggedInTenant; } } int tenantId; try { tenantId = Utils.getTenantId(tenantDomain); } catch (IdentityException e) { vBean = handleError(VerificationBean.ERROR_CODE_UNEXPECTED + " Error while resending confirmation code", e); return vBean; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(tenantId); carbonContext.setTenantDomain(tenantDomain); } try { vBean = processor.verifyConfirmationCode(1, userName, code); if (!vBean.isVerified()) { vBean.setError(VerificationBean.ERROR_CODE_INVALID_CODE); return vBean; } } catch (IdentityException e) { vBean = handleError("Error while validating confirmation code for user : " + userName, e); return vBean; } try { IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty (UserOperationEventListener.class.getName(), IdentityMgtEventListener.class.getName()); boolean isListenerEnable = true; if (identityEventListenerConfig != null) { if (StringUtils.isNotBlank(identityEventListenerConfig.getEnable())) { isListenerEnable = Boolean.parseBoolean(identityEventListenerConfig.getEnable()); } } IdentityMgtConfig config = IdentityMgtConfig.getInstance(); if (isListenerEnable && config.isAuthPolicyAccountLockOnCreation()) { UserDTO userDTO = new UserDTO(UserCoreUtil.addTenantDomainToEntry(userName, tenantDomain)); userDTO.setTenantId(tenantId); UserRecoveryDTO dto = new UserRecoveryDTO(userDTO); dto.setNotification(IdentityMgtConstants.Notification.ACCOUNT_CONFORM); dto.setNotificationType("EMAIL"); vBean = processor.updateConfirmationCode(1, userName, tenantId); dto.setConfirmationCode(vBean.getKey()); NotificationDataDTO notificationDto = processor.notifyWithEmail(dto); vBean.setVerified(notificationDto.isNotificationSent()); // Send email data only if not internally managed. if (!(IdentityMgtConfig.getInstance().isNotificationInternallyManaged())) { vBean.setNotificationData(notificationDto); } } else { vBean.setVerified(true); } } catch (IdentityException e) { vBean = UserIdentityManagementUtil.getCustomErrorMessagesWhenRegistering(e, userName); return vBean; } } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return vBean; } /** * This method used to confirm the self registered user account and unlock it. * * @param username * @param code * @param captcha * @param tenantDomain * @return * @throws IdentityMgtServiceException */ public VerificationBean confirmUserSelfRegistration(String username, String code, CaptchaInfoBean captcha, String tenantDomain) throws IdentityMgtServiceException { VerificationBean bean = new VerificationBean(); if (log.isDebugEnabled()) { log.debug("User registration verification request received with username :" + username); } if (IdentityMgtConfig.getInstance().isCaptchaVerificationInternallyManaged()) { try { CaptchaUtil.processCaptchaInfoBean(captcha); } catch (Exception e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_CAPTCHA + " Error while validating captcha for user : " + username, e); return bean; } } if (!IdentityMgtConfig.getInstance().isSaasEnabled()) { String loggedInTenant = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); if (tenantDomain != null && !tenantDomain.isEmpty() && !loggedInTenant.equals(tenantDomain)) { String msg = "Trying to confirm users in unauthorized tenant space"; log.error(msg); return handleError(VerificationBean.ERROR_CODE_INVALID_TENANT + " " + msg ,null); } if (tenantDomain == null || tenantDomain.isEmpty()) { tenantDomain = loggedInTenant; } } UserDTO userDTO = null; try { userDTO = Utils.processUserId(username + "@" + tenantDomain); } catch (IdentityException e) { bean = handleError(VerificationBean.ERROR_CODE_INVALID_USER + " Error verifying user account for user : " + username, e); return bean; } try { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); carbonContext.setTenantId(userDTO.getTenantId()); carbonContext.setTenantDomain(userDTO.getTenantDomain()); } RecoveryProcessor processor = IdentityMgtServiceComponent.getRecoveryProcessor(); org.wso2.carbon.user.core.UserStoreManager userStoreManager = null; RealmService realmService = IdentityMgtServiceComponent.getRealmService(); int tenantId; try { tenantId = Utils.getTenantId(tenantDomain); if (realmService.getTenantUserRealm(tenantId) != null) { userStoreManager = (org.wso2.carbon.user.core.UserStoreManager) realmService .getTenantUserRealm(tenantId).getUserStoreManager(); if (username != null && username.contains(UserCoreConstants.DOMAIN_SEPARATOR)) { userStoreManager = userStoreManager.getSecondaryUserStoreManager(Utils.getUserStoreDomainName (username)); } } } catch (Exception e) { bean = handleError(VerificationBean.ERROR_CODE_UNEXPECTED + "Error retrieving the user store manager" + " for the tenant : " + tenantDomain, e); return bean; } try { bean = processor.verifyConfirmationCode(1, username, code); if (bean.isVerified()) { UserIdentityManagementUtil.unlockUserAccount(username, userStoreManager); bean.setVerified(true); } else { bean.setVerified(false); bean.setKey(""); log.error("User verification failed against the given confirmation code"); } } catch (IdentityException e) { bean = UserIdentityManagementUtil.getCustomErrorMessagesToVerifyCode(e, username); if (bean.getError() == null) { bean = handleError("Error while validating confirmation code for user : " + username, e); } return bean; } } finally { if (IdentityMgtConfig.getInstance().isSaasEnabled()) { PrivilegedCarbonContext.endTenantFlow(); } } return bean; } private VerificationBean handleError(String error, Exception e) { VerificationBean bean = new VerificationBean(); bean.setVerified(false); if (error != null) { bean.setError(error); log.error(error, e); } else { bean.setError(e.getMessage()); log.error(e.getMessage(), e); } return bean; } private UserChallengesDTO handleChallengesError(String error, Exception e) { UserChallengesDTO bean = new UserChallengesDTO(); if (error != null) { bean.setError(error); log.error(error, e); } else { bean.setError(e.getMessage()); log.error(e.getMessage(), e); } return bean; } private ChallengeQuestionIdsDTO handleChallengeIdError(String error, Exception e) { ChallengeQuestionIdsDTO bean = new ChallengeQuestionIdsDTO(); if (error != null) { bean.setError(error); log.error(error, e); } else { bean.setError(e.getMessage()); log.error(e.getMessage(), e); } return bean; } }