/**
   * Authenticate the user by prompting for the SSO Session Identifier assigned by the SSO Gateway
   * on logon.
   *
   * <p>This method obtains from the gateway, using the provided session identifier, the user
   * associated with such session identifier. Only the NameCallBack is used, since its not a
   * user/password pair but only one value containing the session identifier. Any other callback
   * type is ignored.
   *
   * @return true in all cases since this LoginModule should not be ignored.
   * @exception javax.security.auth.login.FailedLoginException if the authentication fails.
   * @exception javax.security.auth.login.LoginException if this LoginModule is unable to perform
   *     the authentication.
   */
  public boolean login() throws LoginException {

    if (_callbackHandler == null)
      throw new LoginException(
          "Error: no CallbackHandler available "
              + "to garner authentication information from the user");

    Callback[] callbacks = new Callback[2];

    // Just ask for the session identifier
    callbacks[0] = new NameCallback("ssoSessionId");
    callbacks[1] = new PasswordCallback("password", false);

    String ssoSessionId;
    String ssoSessionId2 = null;
    try {
      _callbackHandler.handle(callbacks);
      ssoSessionId = ((NameCallback) callbacks[0]).getName();
      if (((PasswordCallback) callbacks[1]).getPassword() != null)
        ssoSessionId2 = String.valueOf(((PasswordCallback) callbacks[1]).getPassword());

      _requester = "";
      // Check for nulls ?
      SSOAgentRequest request = AbstractSSOAgent._currentRequest.get();
      if (request != null) _requester = request.getRequester();
      else
        logger.warn(
            "No SSO Agent request found in thread local variable, can't identify requester");

    } catch (java.io.IOException ioe) {
      throw new LoginException(ioe.toString());
    } catch (UnsupportedCallbackException uce) {
      throw new LoginException(
          "Error: "
              + uce.getCallback().toString()
              + " not available to garner authentication information "
              + "from the user");
    }

    logger.debug(
        "Requested authentication to gateway by "
            + _requester
            + " using sso session "
            + ssoSessionId
            + "/"
            + ssoSessionId2);

    try {

      if (ssoSessionId2 != null && !ssoSessionId2.equals(ssoSessionId))
        ssoSessionId = ssoSessionId2;

      // If no session is found, ignore this module.
      if (ssoSessionId == null) {
        logger.debug("Session authentication failed : " + ssoSessionId);
        _succeeded = false;
        return false;
      }

      _currentSSOSessionId = ssoSessionId;

      SSOIdentityManagerService im = Lookup.getInstance().lookupSSOAgent().getSSOIdentityManager();
      SSOUser ssoUser = im.findUserInSession(_requester, ssoSessionId);

      logger.debug("Session authentication succeeded : " + ssoSessionId);
      _ssoUserPrincipal = ssoUser;
      _succeeded = true;

    } catch (SSOIdentityException e) {
      // Ignore this ... (user does not exist for this session)
      if (logger.isDebugEnabled()) logger.debug(e.getMessage());
      _succeeded = false;
      return false;

    } catch (Exception e) {
      logger.error("Session authentication failed : " + ssoSessionId, e);
      _succeeded = false;
      clearCredentials();
      throw new FailedLoginException("Fatal error authenticating session : " + e);
    }

    return true;
  }
  /**
   * Return the Principal associated with the specified username and credentials, if there is one;
   * otherwise return null.
   *
   * <p>The method was completely rewritten since the overriden operation, on succesfull
   * authentication, sets as the authenticated Principal a SimplePrincipal instantiated using the
   * provided username. The problem is that in JOSSO the username is a SSO Session Id, not a
   * username. So we need to set the SSOUser returned by the Gateway as the authenticatd Principal.
   * Since the JaasSecurityManager caches the authenticated user using the Principal referring to a
   * JOSSO Session Id, we will need to map, for example when roles are checked against the realm, a
   * user Principal back to its JOSSO Session Identifier Principal. This way the the user and its
   * roles can be retrieved correctly by the JaasSecurityManager.
   *
   * @param username Username of the Principal to look up
   * @param credentials Password or other credentials to use in authenticating this username
   */
  public Principal authenticate(String username, String credentials) {

    logger.debug("Begin authenticate, username=" + username);

    Principal principal = null;
    SSOUser ssoUser = null;
    Principal caller = (Principal) SecurityAssociationValve.userPrincipal.get();
    if (caller == null && username == null && credentials == null) return null;

    try {
      Context securityCtx = null;
      securityCtx = prepareENC();

      if (securityCtx == null) {
        logger.error("No security context for authenticate(String, String)");
        return null;
      }

      // Get the JBoss security manager from the ENC context
      SubjectSecurityManager securityMgr =
          (SubjectSecurityManager) securityCtx.lookup("securityMgr");
      if (!isSSODomain(securityMgr.getSecurityDomain())) {
        // This is not a SSO Security domain, let JBoss realm handle this ...
        return super.authenticate(username, credentials);
      }

      principal = new SimplePrincipal(username);
      char[] passwordChars = null;
      if (credentials != null) passwordChars = credentials.toCharArray();

      String requester = "";
      // Check for nulls ?
      SSOAgentRequest request = AbstractSSOAgent._currentRequest.get();
      if (request == null)
        logger.error(
            "No SSO Agent request found in thread local variable, can't identify requester!");

      SSOAgent agent = Lookup.getInstance().lookupSSOAgent();
      SSOIdentityManagerService im = request.getConfig(agent).getIdentityManagerService();
      if (im == null) im = agent.getSSOIdentityManager();

      requester = request.getRequester();
      ssoUser = im.findUserInSession(requester, username);

      if (ssoUser != null) {
        logger.debug("User: " + username + " is authenticated");

        Subject subject = new Subject();
        subject.getPrincipals().add(ssoUser);
        logger.warn("WARN Cannot identify requester!");
        SSORole[] ssoRolePrincipals = im.findRolesBySSOSessionId(requester, username);
        Group targetGrp = new BaseRoleImpl("Roles");
        for (int i = 0; i < ssoRolePrincipals.length; i++) {
          subject.getPrincipals().add(ssoRolePrincipals[i]);
          targetGrp.addMember(ssoRolePrincipals[i]); // Add user role to "Roles" group
        }
        // Add the "Roles" group to the Subject so that JBoss can fetch user roles.
        subject.getPrincipals().add(targetGrp);

        logger.debug("Authenticated Subject: " + subject);

        // Make the cache aware of the user-session association so that
        // it can handle correctly cache entry lookups.
        // _cachePolicy.attachSessionToUser(principal, ssoUser);

        // Instead of associating the Principal used for authenticating (which is a
        // session id), sets the authenticated principal to the SSOUser part of the
        // Subject returned by the Gateway.
        JBossSecurityAssociationActions.setPrincipalInfo(ssoUser, passwordChars, subject);

        // Get the CallerPrincipal mapping
        RealmMapping rm = (RealmMapping) securityCtx.lookup("realmMapping");
        Principal oldPrincipal = ssoUser;
        principal = rm.getPrincipal(oldPrincipal);
        logger.debug("Mapped from input principal: " + oldPrincipal + " to: " + principal);
        if (!principal.equals(oldPrincipal)) {
          _userPrincipalMap.put(principal, oldPrincipal);
        }

      } else {
        principal = null;
        logger.debug("User: " + username + " is NOT authenticated");
      }
    } catch (NamingException e) {
      principal = null;
      logger.error("Error during authenticate", e);
    } catch (SSOIdentityException e) {
      // Ignore this ... (user does not exist for this session)
      if (logger.isDebugEnabled()) {
        logger.debug(e.getMessage());
      }
      principal = null;
    } catch (Exception e) {
      logger.error("Session authentication failed : " + username, e);
      throw new RuntimeException("Fatal error authenticating session : " + e);
    }
    logger.debug("End authenticate, principal=" + ssoUser);
    return ssoUser;
  }