@Override
 public void logout(JsonSessionState session) {
   logger.trace("Logout Requested");
   for (String shibKey : SHIB_ATTRIBUTES) {
     session.remove(shibKey);
     logger.trace(String.format("Removing Shibboleth Attribute: %s from user.", shibKey));
   }
 }
  /**
   * Logout the provided user
   *
   * @return user The user to logout
   */
  @Override
  public void logout(JsonSessionState session, User user) throws AuthenticationException {
    String source = user.getSource();

    // Clear session
    session.remove("username");
    session.remove("source");

    // SSO Users
    if (sso.containsKey(source)) {
      sso.get(source).logout(session);
      return;
    }

    // Trust token users
    if (source.startsWith(TRUST_TOKEN_PREFIX)) {
      session.remove("validToken");
      return;
    }

    // Standard users
    authManager.logOut(user);
  }
  /**
   * Get user details from SSO connection and set them in the user session.
   *
   * @return boolean: Flag whether a user was actually logged in or not.
   */
  @Override
  public boolean ssoCheckUserDetails(JsonSessionState session) {
    // After the SSO roun-trip, restore any old query parameters we lost
    List<String> currentParams = request.getParameterNames();
    // Cast a copy of keySet() to array to avoid errors as we modify
    String[] oldParams = session.keySet().toArray(new String[0]);
    // Loop through session data...
    for (String key : oldParams) {
      // ... looking for SSO stored params
      if (key.startsWith(SSO_STORAGE_PREFIX)) {
        // Remove our prefix...
        String oldParam = key.replace(SSO_STORAGE_PREFIX, "");
        // ... and check if it survived the trip
        if (!currentParams.contains(oldParam)) {
          // No it didn't, add it to form data... the parameters are
          // already accessible from there in Jython
          String data = (String) session.get(key);
          formData.set(oldParam, data);
          // Don't forget to clear it from the session
          session.remove(key);
        }
      }
    }

    // Check our SSO providers for valid logins
    for (String ssoId : sso.keySet()) {
      sso.get(ssoId).ssoCheckUserDetails(session);
      GenericUser user = (GenericUser) sso.get(ssoId).getUserObject(session);
      if (user != null) {
        session.set("username", user.getUsername());
        session.set("source", ssoId);
        return true;
      }
    }
    return false;
  }
  /**
   * Wrapper method for other SSO methods provided by the security manager. If desired, the security
   * manager can take care of the integration using the default usage pattern, rather then calling
   * them individually.
   *
   * @param session : The session of the current request
   * @param formData : FormData object for the current request
   * @return boolean : True if SSO has redirected, in which case no response should be sent by
   *     Dispatch, otherwise False.
   */
  @Override
  public boolean runSsoIntegration(JsonSessionState session, FormData formData) {
    this.formData = formData;

    // Used in integrating with thrid party systems. They can send us a
    // user, we will log them in via a SSO round-trip, then send them back
    // to the external system
    String returnUrl = request.getParameter("returnUrl");
    if (returnUrl != null) {
      log.info("External redirect requested: '{}'", returnUrl);
      session.set("ssoReturnUrl", returnUrl);
    }

    // The URL parameters can contain a trust token
    String utoken = request.getParameter("token");
    String stoken = (String) session.get("validToken");
    String token = null;
    // Or an 'old' token still in the session
    if (stoken != null) {
      token = stoken;
    }
    // But give the URL priority
    if (utoken != null) {
      token = utoken;
    }
    if (token != null) {
      // Valid token
      if (testTrustToken(session, token)) {
        // Dispatch can continue
        return false;
      }

      // Invalid token
      // Given that trust tokens are designed for system integration
      // we are going to fail with a non-branded error message
      try {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid or expired security token!");
      } catch (IOException ex) {
        log.error("Error sending 403 response to client!");
      }
      // We don't want Dispatch to send a response
      return true;
    }

    // Single Sign-On integration
    try {
      // Instantiate with access to the session
      String ssoId = ssoInit(session);
      if (ssoId != null) {
        // We are logging in, so send them to the SSO portal
        String ssoUrl = ssoGetRemoteLogonURL(session, ssoId);
        if (ssoUrl != null) {
          log.info("Redirect to external URL: '{}'", ssoUrl);
          response.sendRedirect(ssoUrl);
          return true;
        }
      } else {
        // Otherwise, check if we have user's details
        boolean valid = ssoCheckUserDetails(session);
        // If we validly logged in an SSO user, check for an
        // external redirect to third party systems
        if (valid) {
          returnUrl = (String) session.get("ssoReturnUrl");
          if (returnUrl != null) {
            log.info("Redirect to external URL: '{}'", returnUrl);
            session.remove("ssoReturnUrl");
            response.sendRedirect(returnUrl);
            return true;
          }
        }
      }
    } catch (Exception ex) {
      log.error("SSO Error!", ex);
    }

    return false;
  }