/*
   * TODO - Important. Refactor this method and use recoveryWithNotification instead.
   */
  public NotificationDataDTO notifyWithEmail(UserRecoveryDTO notificationBean)
      throws IdentityException {

    String notificationAddress;

    String confirmationKey = null;
    NotificationSendingModule module = null;

    String userId = notificationBean.getUserId();
    String domainName = notificationBean.getTenantDomain();
    int tenantId = notificationBean.getTenantId();
    confirmationKey = notificationBean.getConfirmationCode();
    String userStore = IdentityUtil.extractDomainFromName(userId);
    String userName = UserCoreUtil.removeDomainFromName(userId);

    NotificationDataDTO notificationData = new NotificationDataDTO();

    String type = notificationBean.getNotificationType();
    if (type != null) {
      module = modules.get(type);
    }

    if (module == null) {
      module = defaultModule;
    }

    NotificationData emailNotificationData = new NotificationData();
    String emailTemplate = null;
    notificationAddress = module.getNotificationAddress(userId, tenantId);

    if ((notificationAddress == null) || (notificationAddress.trim().length() < 0)) {
      log.warn("Notification address is not defined for user " + userId);
    }

    String firstName =
        Utils.getClaimFromUserStoreManager(userId, tenantId, "http://wso2.org/claims/givenname");
    emailNotificationData.setTagData(FIRST_NAME, firstName);
    emailNotificationData.setTagData(USER_STORE_DOMAIN, userStore);
    emailNotificationData.setTagData(USER_NAME, userName);
    emailNotificationData.setTagData(TENANT_DOMAIN, domainName);
    emailNotificationData.setSendTo(notificationAddress);

    Config config = null;
    ConfigBuilder configBuilder = ConfigBuilder.getInstance();
    try {
      config = configBuilder.loadConfiguration(ConfigType.EMAIL, StorageType.REGISTRY, tenantId);
    } catch (Exception e1) {
      throw new IdentityException(
          "Error occurred while loading email templates for user : "******"EMAIL", emailTemplate, emailNotificationData);
    } catch (Exception e) {
      throw new IdentityException(
          "Error occurred while creating notification from email template : " + emailTemplate, e);
    }

    notificationData.setNotificationAddress(notificationAddress);
    notificationData.setUserId(userId);
    notificationData.setDomainName(domainName);
    notificationData.setNotificationType(notificationBean.getNotificationType());

    if (IdentityMgtConfig.getInstance().isNotificationInternallyManaged()) {
      module.setNotificationData(notificationData);
      module.setNotification(emailNotification);
      notificationSender.sendNotification(module);
      notificationData.setNotificationSent(true);
    } else {
      notificationData.setNotificationSent(false);
      notificationData.setNotificationCode(confirmationKey);
    }

    return notificationData;
  }
  /**
   * Processing recovery
   *
   * @param recoveryDTO class that contains user and tenant Information
   * @return true if the reset request is processed successfully.
   * @throws IdentityException if fails
   */
  public NotificationDataDTO recoverWithNotification(UserRecoveryDTO recoveryDTO)
      throws IdentityException {

    String notificationAddress;
    String secretKey = null;
    String confirmationKey = null;
    NotificationSendingModule module = null;
    boolean persistData = true;
    String userId = recoveryDTO.getUserId();
    String domainName = recoveryDTO.getTenantDomain();
    int tenantId = recoveryDTO.getTenantId();
    String userStore = IdentityUtil.extractDomainFromName(userId);
    String userName = UserCoreUtil.removeDomainFromName(userId);
    TenantManager tenantManager = IdentityMgtServiceComponent.getRealmService().getTenantManager();
    try {
      Tenant tenant = tenantManager.getTenant(tenantId);
      if (tenant != null) {
        domainName = tenant.getDomain();
      }

    } catch (UserStoreException e) {
      if (log.isDebugEnabled()) {
        log.debug("No Tenant domain for tenant id " + tenantId, e);
      }
    }
    NotificationDataDTO notificationData = new NotificationDataDTO();
    String internalCode = null;

    String type = recoveryDTO.getNotificationType();
    if (type != null) {
      module = modules.get(type);
    }

    if (module == null) {
      module = defaultModule;
    }

    NotificationData emailNotificationData = new NotificationData();
    String emailTemplate = null;

    notificationAddress = Utils.getEmailAddressForUser(userId, tenantId);
    String firstName =
        Utils.getClaimFromUserStoreManager(userId, tenantId, "http://wso2.org/claims/givenname");
    emailNotificationData.setTagData(FIRST_NAME, firstName);
    emailNotificationData.setTagData(USER_STORE_DOMAIN, userStore);
    emailNotificationData.setTagData(USER_NAME, userName);
    emailNotificationData.setTagData(TENANT_DOMAIN, domainName);

    if ((notificationAddress == null) || (notificationAddress.trim().length() < 0)) {
      throw new IdentityException(
          "Notification sending failure. Notification address is not defined for user : "******"Building notification with data - First name: "
              + firstName
              + " User name: "
              + userId
              + " Send To: "
              + notificationAddress);
    }

    Config config = null;
    ConfigBuilder configBuilder = ConfigBuilder.getInstance();
    try {
      config = configBuilder.loadConfiguration(ConfigType.EMAIL, StorageType.REGISTRY, tenantId);
    } catch (Exception e1) {
      throw new IdentityException("Error while loading email templates for user : "******"Error while getting user's external code string.", e);
        }
        secretKey = UUIDGenerator.generateUUID();
        emailNotificationData.setTagData(CONFIRMATION_CODE, confirmationKey);
        emailTemplate =
            config.getProperty(IdentityMgtConstants.Notification.PASSWORD_RESET_RECOVERY);

      } else if (IdentityMgtConstants.Notification.ACCOUNT_CONFORM.equals(notification)) {
        confirmationKey = UUIDGenerator.generateUUID();
        secretKey = UUIDGenerator.generateUUID();
        emailNotificationData.setTagData(CONFIRMATION_CODE, confirmationKey);
        emailTemplate = config.getProperty(IdentityMgtConstants.Notification.ACCOUNT_CONFORM);

      } else if (IdentityMgtConstants.Notification.TEMPORARY_PASSWORD.equals(notification)) {
        String temporaryPassword = recoveryDTO.getTemporaryPassword(); // TODO
        if (temporaryPassword == null || temporaryPassword.trim().length() < 1) {
          char[] chars = IdentityMgtConfig.getInstance().getPasswordGenerator().generatePassword();
          temporaryPassword = new String(chars);
        }
        Utils.updatePassword(userId, tenantId, temporaryPassword);
        emailNotificationData.setTagData(TEMPORARY_PASSWORD, temporaryPassword);
        emailTemplate = config.getProperty(IdentityMgtConstants.Notification.TEMPORARY_PASSWORD);
        persistData = false;
      } else if (IdentityMgtConstants.Notification.ACCOUNT_UNLOCK.equals(notification)) {
        emailTemplate = config.getProperty(IdentityMgtConstants.Notification.ACCOUNT_UNLOCK);
        persistData = false;
      } else if (IdentityMgtConstants.Notification.ACCOUNT_ID_RECOVERY.equals(notification)) {
        emailTemplate = config.getProperty(IdentityMgtConstants.Notification.ACCOUNT_ID_RECOVERY);
        persistData = false;
      } else if (IdentityMgtConstants.Notification.ASK_PASSWORD.equals(notification)) {
        if (firstName == null || firstName.isEmpty()) {
          emailNotificationData.setTagData(FIRST_NAME, userId);
        }
        internalCode = generateUserCode(2, userId);
        try {
          confirmationKey = getUserExternalCodeStr(internalCode);
        } catch (Exception e) {
          throw new IdentityException("Error while with recovering with password.", e);
        }
        secretKey = UUIDGenerator.generateUUID();
        emailNotificationData.setTagData(CONFIRMATION_CODE, confirmationKey);
        emailTemplate = config.getProperty(IdentityMgtConstants.Notification.ASK_PASSWORD);
      }

      if (log.isDebugEnabled()) {
        log.debug("Notification type: " + notification);
      }
    }

    Notification emailNotification = null;
    try {
      emailNotification =
          NotificationBuilder.createNotification("EMAIL", emailTemplate, emailNotificationData);
    } catch (Exception e) {
      throw new IdentityException("Error when creating notification for user : " + userId, e);
    }

    notificationData.setNotificationAddress(notificationAddress);
    notificationData.setUserId(userId);
    notificationData.setDomainName(domainName);
    notificationData.setNotificationType(recoveryDTO.getNotificationType());

    if (persistData) {
      UserRecoveryDataDO recoveryDataDO =
          new UserRecoveryDataDO(userId, tenantId, internalCode, secretKey);
      dataStore.invalidate(userId, tenantId);
      dataStore.store(recoveryDataDO);
    }

    if (IdentityMgtConfig.getInstance().isNotificationInternallyManaged()) {
      module.setNotificationData(notificationData);
      module.setNotification(emailNotification);
      notificationSender.sendNotification(module);
      notificationData.setNotificationSent(true);
    } else {
      notificationData.setNotificationSent(false);
      notificationData.setNotificationCode(confirmationKey);
    }

    return notificationData;
  }
  /**
   * This method locks the created accounts based on the account policies or based on the account
   * confirmation method being used. Two account confirmation methods are used : Temporary Password
   * and Verification Code. In the case of temporary password is used the temporary password will be
   * emailed to the user. In the case of verification code, the code will be emailed to the user.
   * The security questions filter ad doPreAddUser will be persisted in this method.
   */
  @Override
  public boolean doPostAddUser(
      String userName,
      Object credential,
      String[] roleList,
      Map<String, String> claims,
      String profile,
      UserStoreManager userStoreManager)
      throws UserStoreException {
    if (log.isDebugEnabled()) {
      log.debug("Post add user is called in IdentityMgtEventListener");
    }
    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }
    // reading the value from the thread local
    UserIdentityClaimsDO userIdentityClaimsDO =
        (UserIdentityClaimsDO) threadLocalProperties.get().get(USER_IDENTITY_DO);

    if (config.isEnableUserAccountVerification()) {

      // empty password account creation
      if (threadLocalProperties.get().containsKey(EMPTY_PASSWORD_USED)) {
        // store identity data
        userIdentityClaimsDO.setAccountLock(false).setPasswordTimeStamp(System.currentTimeMillis());
        try {
          module.store(userIdentityClaimsDO, userStoreManager);
        } catch (IdentityException e) {
          throw new UserStoreException("Error while doPostAddUser", e);
        }
        // store identity metadata
        UserRecoveryDataDO metadataDO = new UserRecoveryDataDO();
        metadataDO
            .setUserName(userName)
            .setTenantId(userStoreManager.getTenantId())
            .setCode((String) credential);
        //				try {
        //	                UserIdentityManagementUtil.storeUserIdentityMetadata(metadataDO);
        //                } catch (IdentityException e) {
        //                	throw new UserStoreException("Error while doPreAddUser", e);
        //                }

        // set recovery data
        RecoveryProcessor processor = new RecoveryProcessor();
        VerificationBean verificationBean = new VerificationBean();

        try {
          verificationBean =
              processor.updateConfirmationCode(1, userName, userStoreManager.getTenantId());
        } catch (IdentityException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }

        // preparing a bean to send the email
        UserIdentityMgtBean bean = new UserIdentityMgtBean();
        bean.setUserId(userName)
            .setConfirmationCode(verificationBean.getKey())
            .setRecoveryType(IdentityMgtConstants.Notification.TEMPORARY_PASSWORD)
            .setEmail(claims.get(config.getAccountRecoveryClaim()));

        UserRecoveryDTO recoveryDto = new UserRecoveryDTO(userName);
        recoveryDto.setNotification(IdentityMgtConstants.Notification.ASK_PASSWORD);
        recoveryDto.setNotificationType("EMAIL");
        recoveryDto.setTenantId(userStoreManager.getTenantId());
        recoveryDto.setConfirmationCode(verificationBean.getKey());

        NotificationDataDTO notificationDto = null;

        try {
          notificationDto = processor.recoverWithNotification(recoveryDto);
        } catch (IdentityException e) {
          if (log.isDebugEnabled()) {
            log.debug(e.getMessage());
          }
          throw new UserStoreException("Error while sending notification. " + e.getMessage());
        }

        if (notificationDto != null && notificationDto.isNotificationSent()) {
          return true;
        } else {
          return false;
        }

        // sending email
        //				UserIdentityManagementUtil.notifyViaEmail(bean);

      } else {
        // none-empty passwords. lock account and persist
        /*				This scenario needs to be validated.
        * 				userIdentityClaimsDO.setAccountLock(true)
        			                    .setPasswordTimeStamp(System.currentTimeMillis());
        			try {
        				UserIdentityManagementUtil.storeUserIdentityClaims(userIdentityClaimsDO, userStoreManager);
        			} catch (IdentityException e) {
        				throw new UserStoreException("Error while doPostAddUser", e);
        			}
        			String confirmationCode = UserIdentityManagementUtil.generateRandomConfirmationCode();
        			// store identity metadata
        			UserRecoveryDataDO metadataDO = new UserRecoveryDataDO();
        			metadataDO.setUserName(userName).setTenantId(userStoreManager.getTenantId())
        			          .setCode(confirmationCode);
        			try {
                        UserIdentityManagementUtil.storeUserIdentityMetadata(metadataDO);
                       } catch (IdentityException e) {
                       	throw new UserStoreException("Error while doPostAddUser", e);
                       }
        			// sending a mail with the confirmation code
        			UserIdentityMgtBean bean = new UserIdentityMgtBean();
        			bean.setUserId(userName)
        			    .setRecoveryType(IdentityMgtConstants.Notification.ACCOUNT_CONFORM)
        			    .setConfirmationCode(confirmationCode);
        			UserIdentityManagementUtil.notifyViaEmail(bean);
        			return true; */
      }
    }
    // No account recoveries are defined, no email will be sent.
    if (config.isAuthPolicyAccountLockOnCreation()) {
      // accounts are locked. Admin should unlock
      userIdentityClaimsDO.setAccountLock(true);
      userIdentityClaimsDO.setPasswordTimeStamp(System.currentTimeMillis());
      try {
        config.getIdentityDataStore().store(userIdentityClaimsDO, userStoreManager);
      } catch (IdentityException e) {
        throw new UserStoreException("Error while doPostAddUser", e);
      }
    }
    return true;
  }
  /**
   * 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 (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});
      }

      IdentityEventListener identityEventListener =
          IdentityUtil.readEventListenerProperty(
              UserOperationEventListener.class.getName(), IdentityMgtEventListener.class.getName());

      boolean isListenerEnable = true;

      if (identityEventListener != null) {
        if (StringUtils.isNotBlank(identityEventListener.getEnable())) {
          isListenerEnable = Boolean.parseBoolean(identityEventListener.getEnable());
        }
      }

      IdentityMgtConfig config = IdentityMgtConfig.getInstance();

      if (isListenerEnable && config.isAuthPolicyAccountLockOnCreation()) {
        UserDTO userDTO = new UserDTO(userName);
        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) {
      UserIdentityManagementUtil.getCustomErrorMessages(e, userName);
      // Rollback if user exists
      try {
        if (userStoreManager.isExistingUser(userName)) {
          userStoreManager.deleteUser(userName);
        }
      } catch (org.wso2.carbon.user.core.UserStoreException e1) {
        UserIdentityManagementUtil.getCustomErrorMessages(e1, userName);
      }

      return vBean;
    }

    return vBean;
  }
  /**
   * 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(userName);
        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;
  }
  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 : "******" notification type :"
              + notificationType);
    }
    try {
      userDTO = Utils.processUserId(username);
    } catch (IdentityException e) {
      bean =
          handleError(VerificationBean.ERROR_CODE_INVALID_USER + " invalid user : "******"Invalid user is trying to recover the password with username : "******" Invalid user is trying to recover the password with username : "******" invalid confirmation code for user : "******"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 =
          handleError(
              VerificationBean.ERROR_CODE_UNEXPECTED
                  + " Error when sending recovery message for user: "
                  + username,
              e);
      return bean;
    } finally {
      if (IdentityMgtConfig.getInstance().isSaasEnabled()) {
        PrivilegedCarbonContext.endTenantFlow();
      }
    }
    return bean;
  }