/**
   * This method checks if the updating claim is an user identity data or security question.
   * Identity data and security questions are updated by the identity store, therefore they will not
   * be added to the user store. Other claims are skipped to the set or update.
   */
  @Override
  public boolean doPreSetUserClaimValue(
      String userName,
      String claimURI,
      String claimValue,
      String profileName,
      UserStoreManager userStoreManager)
      throws UserStoreException {
    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }
    UserIdentityDataStore identityDataStore =
        IdentityMgtConfig.getInstance().getIdentityDataStore();
    UserIdentityClaimsDO identityDTO = null;

    // security questions and identity claims are updated at the identity store
    if (claimURI.contains(UserCoreConstants.ClaimTypeURIs.CHALLENGE_QUESTION_URI)
        || claimURI.contains(UserCoreConstants.ClaimTypeURIs.IDENTITY_CLAIM_URI)) {
      //            identityDTO = identityDataStore.load(userName, userStoreManager);
      //            if (identityDTO == null) { // no such user is added to the system
      //                return false;
      //            }
      //            modified - why is it adding claim in preSet method?
      //            identityDTO.setUserIdentityDataClaim(claimURI, claimValue);
      //            return false; // this will cause
      //			  the whole listner to return and fail adding the cliam in doSetUserClaim
      return true;
    } else {
      // a simple user claim. add it to the user store
      return true;
    }
  }
  public IdentityMgtEventListener() {

    module = IdentityMgtConfig.getInstance().getIdentityDataStore();
    String adminUserName =
        IdentityMgtServiceComponent.getRealmService()
            .getBootstrapRealmConfiguration()
            .getAdminUserName();
    try {
      IdentityMgtConfig config = IdentityMgtConfig.getInstance();

      // Get the policy registry with the loaded policies.
      policyRegistry = config.getPolicyRegistry();

      if (config.isListenerEnable()) {

        UserStoreManager userStoreMng =
            IdentityMgtServiceComponent.getRealmService().getBootstrapRealm().getUserStoreManager();
        if (!userStoreMng.isReadOnly()) {

          userStoreMng.setUserClaimValue(
              adminUserName, UserIdentityDataStore.ACCOUNT_LOCK, Boolean.toString(false), null);
        }
      }
    } catch (UserStoreException e) {
      log.error("Error while init identity listener", e);
    }
  }
  /** This method is used to check pre conditions when changing the user password. */
  public boolean doPreUpdateCredential(
      String userName,
      Object newCredential,
      Object oldCredential,
      UserStoreManager userStoreManager)
      throws UserStoreException {

    if (log.isDebugEnabled()) {
      log.debug("Pre update credential is called in IdentityMgtEventListener");
    }

    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }

    try {
      // Enforcing the password policies.
      if (newCredential != null
          && (newCredential instanceof String && (newCredential.toString().trim().length() > 0))) {
        policyRegistry.enforcePasswordPolicies(newCredential.toString(), userName);
      }

    } catch (PolicyViolationException pe) {
      log.error(pe.getMessage());
      throw new UserStoreException(pe.getMessage());
    }

    return true;
  }
  /**
   * This method is used when the admin is updating the credentials with an empty credential. A
   * random password will be generated and will be mailed to the user.
   */
  @Override
  public boolean doPreUpdateCredentialByAdmin(
      String userName, Object newCredential, UserStoreManager userStoreManager)
      throws UserStoreException {

    if (log.isDebugEnabled()) {
      log.debug("Pre update credential by admin is called in IdentityMgtEventListener");
    }
    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }

    try {
      // Enforcing the password policies.
      if (newCredential != null
          && (newCredential instanceof StringBuffer
              && (newCredential.toString().trim().length() > 0))) {
        policyRegistry.enforcePasswordPolicies(newCredential.toString(), userName);
      }

    } catch (PolicyViolationException pe) {
      log.error(pe.getMessage());
      throw new UserStoreException(pe.getMessage());
    }

    if (newCredential == null
        || (newCredential instanceof StringBuffer
            && ((StringBuffer) newCredential).toString().trim().length() < 1)) {

      if (!config.isEnableTemporaryPassword()) {
        log.error("Empty passwords are not allowed");
        return false;
      }
      if (log.isDebugEnabled()) {
        log.debug("Credentials are null. Using a temporary password as credentials");
      }
      // temporary passwords will be used
      char[] temporaryPassword = UserIdentityManagementUtil.generateTemporaryPassword();
      // setting the password value
      ((StringBuffer) newCredential)
          .replace(0, temporaryPassword.length, new String(temporaryPassword));

      UserIdentityMgtBean bean = new UserIdentityMgtBean();
      bean.setUserId(userName);
      bean.setConfirmationCode(newCredential.toString());
      bean.setRecoveryType(IdentityMgtConstants.Notification.TEMPORARY_PASSWORD);
      log.debug("Sending the tempory password to the user " + userName);
      UserIdentityManagementUtil.notifyViaEmail(bean);
    } else {
      log.debug("Updating credentials of user " + userName + " by admin with a non-empty password");
    }
    return true;
  }
 /** Adding the user identity data to the claims set */
 @Override
 public boolean doPostGetUserClaimValues(
     String userName,
     String[] claims,
     String profileName,
     Map<String, String> claimMap,
     UserStoreManager storeManager)
     throws UserStoreException {
   IdentityMgtConfig config = IdentityMgtConfig.getInstance();
   if (!config.isListenerEnable()) {
     return true;
   }
   if (claimMap == null) {
     claimMap = new HashMap<String, String>();
   }
   UserIdentityDataStore identityDataStore =
       IdentityMgtConfig.getInstance().getIdentityDataStore();
   // check if there are identity claims
   boolean containsIdentityClaims = false;
   for (String claim : claims) {
     if (claim.contains(UserCoreConstants.ClaimTypeURIs.CHALLENGE_QUESTION_URI)
         || claim.contains(UserCoreConstants.ClaimTypeURIs.IDENTITY_CLAIM_URI)) {
       containsIdentityClaims = true;
       break;
     }
   }
   // if there are no identity claims, let it go
   if (!containsIdentityClaims) {
     return true;
   }
   // there is/are identity claim/s . load the dto
   UserIdentityClaimsDO identityDTO = identityDataStore.load(userName, storeManager);
   // if no user identity data found, just continue
   if (identityDTO == null) {
     return true;
   }
   // data found, add the values for security questions and identity claims
   for (String claim : claims) {
     if (identityDTO.getUserDataMap().containsKey(claim)) {
       claimMap.put(claim, identityDTO.getUserDataMap().get(claim));
     }
   }
   return true;
 }
  /**
   * As in the above method the user account lock claim, primary challenges claim will be separately
   * handled. Identity claims will be removed from the claim set before adding claims to the user
   * store.
   */
  @Override
  public boolean doPreSetUserClaimValues(
      String userName,
      Map<String, String> claims,
      String profileName,
      UserStoreManager userStoreManager)
      throws UserStoreException {
    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }
    UserIdentityDataStore identityDataStore =
        IdentityMgtConfig.getInstance().getIdentityDataStore();
    //		  To fix https://wso2.org/jira/browse/IDENTITY-1227
    UserIdentityClaimsDO identityDTO = new UserIdentityClaimsDO(userName);

    //        identityDTO = identityDataStore.load(userName, userStoreManager);
    //        if (identityDTO == null) { // user doesn't exist in the system
    //            return false;
    //        }

    // removing identity claims and security questions
    Iterator<Entry<String, String>> it = claims.entrySet().iterator();
    while (it.hasNext()) {

      Map.Entry<String, String> claim = it.next();

      if (claim.getKey().contains(UserCoreConstants.ClaimTypeURIs.CHALLENGE_QUESTION_URI)
          || claim.getKey().contains(UserCoreConstants.ClaimTypeURIs.IDENTITY_CLAIM_URI)) {
        identityDTO.setUserIdentityDataClaim(claim.getKey(), claim.getValue());
        it.remove();
      }
    }

    // storing the identity claims and security questions
    try {
      identityDataStore.store(identityDTO, userStoreManager);
    } catch (IdentityException e) {
      throw new UserStoreException("Error while doPreSetUserClaimValues", e);
    }
    return true;
  }
  /** Deleting user from the identity database. What are the registry keys ? */
  @Override
  public boolean doPostDeleteUser(String userName, UserStoreManager userStoreManager)
      throws UserStoreException {

    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }
    // remove from the identity store
    try {
      IdentityMgtConfig.getInstance().getIdentityDataStore().remove(userName, userStoreManager);
    } catch (IdentityException e) {
      throw new UserStoreException("Error while doPostDeleteUser", e);
    }
    // deleting registry meta-data
    UserRegistry registry = null;
    try {
      registry =
          IdentityMgtServiceComponent.getRegistryService()
              .getConfigSystemRegistry(userStoreManager.getTenantId());
      String identityKeyMgtPath =
          IdentityMgtConstants.IDENTITY_MANAGEMENT_KEYS
              + RegistryConstants.PATH_SEPARATOR
              + userStoreManager.getTenantId()
              + RegistryConstants.PATH_SEPARATOR
              + userName;

      if (registry.resourceExists(identityKeyMgtPath)) {
        registry.delete(identityKeyMgtPath);
      }
    } catch (RegistryException e) {
      log.error(
          "Error while deleting recovery data for user : "******" in tenant : "
              + userStoreManager.getTenantId(),
          e);
    }
    return true;
  }
  /**
   * 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 will set the default/random password if the password provided is null. The thread
   * local parameter EMPTY_PASSWORD_USED will be used to track if the password empty in the
   * doPostAddUser. This method will filter the security question URIs from claims and put those to
   * the thread local properties.
   */
  @Override
  public boolean doPreAddUser(
      String userName,
      Object credential,
      String[] roleList,
      Map<String, String> claims,
      String profile,
      UserStoreManager userStoreManager)
      throws UserStoreException {

    if (log.isDebugEnabled()) {
      log.debug("Pre add user is called in IdentityMgtEventListener");
    }
    IdentityMgtConfig config = IdentityMgtConfig.getInstance();
    if (!config.isListenerEnable()) {
      return true;
    }

    try {
      // Enforcing the password policies.
      if (credential != null
          && (credential instanceof StringBuffer && (credential.toString().trim().length() > 0))) {
        policyRegistry.enforcePasswordPolicies(credential.toString(), userName);
      }

    } catch (PolicyViolationException pe) {
      log.error(pe.getMessage());
      throw new UserStoreException(pe.getMessage());
    }

    // empty password account creation
    if (credential == null
        || (credential instanceof StringBuffer && (credential.toString().trim().length() < 1))) {

      if (!config.isEnableTemporaryPassword()) {
        log.error("Empty passwords are not allowed");
        return false;
      }
      if (log.isDebugEnabled()) {
        log.debug("Credentials are null. Using a temporary password as credentials");
      }
      // setting the thread-local to check in doPostAddUser
      threadLocalProperties.get().put(EMPTY_PASSWORD_USED, true);
      // temporary passwords will be used
      char[] temporaryPassword = UserIdentityManagementUtil.generateTemporaryPassword();

      // setting the password value
      ((StringBuffer) credential)
          .replace(0, temporaryPassword.length, new String(temporaryPassword));
    }

    // Filtering security question URIs from claims and add them to the thread local dto
    Map<String, String> userDataMap = new HashMap<String, String>();

    // TODO why challenge Q
    Iterator<Entry<String, String>> it = claims.entrySet().iterator();
    while (it.hasNext()) {

      Map.Entry<String, String> claim = it.next();

      if (claim.getKey().contains(UserCoreConstants.ClaimTypeURIs.CHALLENGE_QUESTION_URI)
          || claim.getKey().contains(UserCoreConstants.ClaimTypeURIs.IDENTITY_CLAIM_URI)) {
        userDataMap.put(claim.getKey(), claim.getValue());
        it.remove();
      }
    }

    UserIdentityClaimsDO identityDTO = new UserIdentityClaimsDO(userName, userDataMap);
    // adding dto to thread local to be read again from the doPostAddUser method
    threadLocalProperties.get().put(USER_IDENTITY_DO, identityDTO);
    return true;
  }