/**
  * Login the passed user and require a set of certain roles, the used needs to have to login here.
  *
  * @param sLoginName Login name of the user to log-in. May be <code>null</code>.
  * @param sPlainTextPassword Plain text password to use. May be <code>null</code>.
  * @param aRequiredRoleIDs A set of required role IDs, the user needs to have. May be <code>null
  *     </code>.
  * @return Never <code>null</code> login status.
  */
 @Nonnull
 public ELoginResult loginUser(
     @Nullable final String sLoginName,
     @Nullable final String sPlainTextPassword,
     @Nullable final Collection<String> aRequiredRoleIDs) {
   // Try to resolve the user
   final IUser aUser = PhotonSecurityManager.getUserMgr().getUserOfLoginName(sLoginName);
   if (aUser == null) {
     AuditHelper.onAuditExecuteFailure("login", sLoginName, "no-such-loginname");
     return ELoginResult.USER_NOT_EXISTING;
   }
   return loginUser(aUser, sPlainTextPassword, aRequiredRoleIDs);
 }
    private void readObject(@Nonnull final ObjectInputStream aOIS)
        throws IOException, ClassNotFoundException {
      aOIS.defaultReadObject();

      // Resolve user ID
      if (m_sUserID != null) {
        m_aUser = PhotonSecurityManager.getUserMgr().getUserOfID(m_sUserID);
        if (m_aUser == null)
          throw new IllegalStateException("Failed to resolve user with ID '" + m_sUserID + "'");
      }

      // Resolve manager
      m_aOwningMgr = LoggedInUserManager.getInstance();
    }
  /**
   * Login the passed user and require a set of certain roles, the used needs to have to login here.
   *
   * @param aUser The user to log-in. May be <code>null</code>. When the user is <code>null</code>
   *     the login must fail.
   * @param sPlainTextPassword Plain text password to use. May be <code>null</code>.
   * @param aRequiredRoleIDs A set of required role IDs, the user needs to have. May be <code>null
   *     </code>.
   * @return Never <code>null</code> login status.
   */
  @Nonnull
  public ELoginResult loginUser(
      @Nullable final IUser aUser,
      @Nullable final String sPlainTextPassword,
      @Nullable final Collection<String> aRequiredRoleIDs) {
    if (aUser == null) return ELoginResult.USER_NOT_EXISTING;

    final String sUserID = aUser.getID();

    // Deleted user?
    if (aUser.isDeleted()) {
      AuditHelper.onAuditExecuteFailure("login", sUserID, "user-is-deleted");
      return _onLoginError(sUserID, ELoginResult.USER_IS_DELETED);
    }

    // Disabled user?
    if (aUser.isDisabled()) {
      AuditHelper.onAuditExecuteFailure("login", sUserID, "user-is-disabled");
      return _onLoginError(sUserID, ELoginResult.USER_IS_DISABLED);
    }

    // Are all roles present?
    if (!SecurityHelper.hasUserAllRoles(sUserID, aRequiredRoleIDs)) {
      AuditHelper.onAuditExecuteFailure(
          "login",
          sUserID,
          "user-is-missing-required-roles",
          StringHelper.getToString(aRequiredRoleIDs));
      return _onLoginError(sUserID, ELoginResult.USER_IS_MISSING_ROLE);
    }

    // Check the password
    final UserManager aUserMgr = PhotonSecurityManager.getUserMgr();
    if (!aUserMgr.areUserIDAndPasswordValid(sUserID, sPlainTextPassword)) {
      AuditHelper.onAuditExecuteFailure("login", sUserID, "invalid-password");
      return _onLoginError(sUserID, ELoginResult.INVALID_PASSWORD);
    }

    // Check if the password hash needs to be updated
    final String sExistingPasswordHashAlgorithmName = aUser.getPasswordHash().getAlgorithmName();
    final String sDefaultPasswordHashAlgorithmName =
        GlobalPasswordSettings.getPasswordHashCreatorManager()
            .getDefaultPasswordHashCreatorAlgorithmName();
    if (!sExistingPasswordHashAlgorithmName.equals(sDefaultPasswordHashAlgorithmName)) {
      // This implicitly implies using the default hash creator algorithm
      // This automatically saves the file
      aUserMgr.setUserPassword(sUserID, sPlainTextPassword);
      s_aLogger.info(
          "Updated password hash of user '"
              + sUserID
              + "' from algorithm '"
              + sExistingPasswordHashAlgorithmName
              + "' to '"
              + sDefaultPasswordHashAlgorithmName
              + "'");
    }

    boolean bLoggedOutUser = false;
    LoginInfo aInfo;
    m_aRWLock.writeLock().lock();
    try {
      if (m_aLoggedInUsers.containsKey(sUserID)) {
        // The user is already logged in
        if (isLogoutAlreadyLoggedInUser()) {
          // Explicitly log out
          logoutUser(sUserID);

          // Just a short check
          if (m_aLoggedInUsers.containsKey(sUserID))
            throw new IllegalStateException("Failed to logout '" + sUserID + "'");

          AuditHelper.onAuditExecuteSuccess("logout-in-login", sUserID);
          bLoggedOutUser = true;
        } else {
          AuditHelper.onAuditExecuteFailure("login", sUserID, "user-already-logged-in");
          return _onLoginError(sUserID, ELoginResult.USER_ALREADY_LOGGED_IN);
        }
      }

      final SessionUserHolder aSUH = SessionUserHolder.getInstance();
      if (aSUH.hasUser()) {
        // This session already has a user
        s_aLogger.warn(
            "The session user holder already has the user ID '"
                + aSUH.getUserID()
                + "' so the new ID '"
                + sUserID
                + "' will not be set!");
        AuditHelper.onAuditExecuteFailure("login", sUserID, "session-already-has-user");
        return _onLoginError(sUserID, ELoginResult.SESSION_ALREADY_HAS_USER);
      }

      aInfo = new LoginInfo(aUser, ScopeManager.getSessionScope());
      m_aLoggedInUsers.put(sUserID, aInfo);
      aSUH.setUser(this, aUser);
    } finally {
      m_aRWLock.writeLock().unlock();
    }

    s_aLogger.info(
        "Logged in user '" + sUserID + "' with login name '" + aUser.getLoginName() + "'");
    AuditHelper.onAuditExecuteSuccess("login", sUserID, aUser.getLoginName());

    // Execute callback as the very last action
    for (final IUserLoginCallback aUserLoginCallback : m_aUserLoginCallbacks.getAllCallbacks())
      try {
        aUserLoginCallback.onUserLogin(aInfo);
      } catch (final Throwable t) {
        s_aLogger.error(
            "Failed to invoke onUserLogin callback on "
                + aUserLoginCallback.toString()
                + "("
                + aInfo.toString()
                + ")",
            t);
      }

    return bLoggedOutUser ? ELoginResult.SUCCESS_WITH_LOGOUT : ELoginResult.SUCCESS;
  }