private OMElement handleSLORequest(MessageContext messageContext, LogoutRequest logoutRequest) {

    // Get the session index from the SLORequest and remove the relevant session.
    String sessionIndex = logoutRequest.getSessionIndexes().get(0).getSessionIndex();

    String sessionId = CacheManager.getInstance().getSessionIndexMappingCache().get(sessionIndex);

    if (sessionId != null) {
      GatewayUtils.logWithRequestInfo(
          log,
          messageContext,
          String.format(
              "Found a session id (md5 : '%s')for the given session index in the SLO request: '%s'. Clearing the session",
              GatewayUtils.getMD5Hash(sessionId), sessionIndex));
      SessionStore.getInstance().removeSession(sessionId);
      CacheManager.getInstance().getSessionIndexMappingCache().remove(sessionIndex);
    } else {
      GatewayUtils.logWithRequestInfo(
          log,
          messageContext,
          String.format(
              "Couldn't find a session id for the given session index : '%s'", sessionIndex));
    }

    OMFactory fac = OMAbstractFactory.getOMFactory();
    OMNamespace ns = fac.createOMNamespace("http://wso2.org/appm", "appm");
    OMElement payload = fac.createOMElement("SLOResponse", ns);

    OMElement errorMessage = fac.createOMElement("message", ns);
    errorMessage.setText("SLORequest has been successfully processed by WSO2 App Manager");

    payload.addChild(errorMessage);

    return payload;
  }
  /**
   * Pass THE SAML response and returns an authorized cookie to access IDP admin services
   *
   * @param samlResponse
   * @return Cookie to access IDP admin services
   */
  private String getAuthenticatedCookieFromIdP(String samlResponse) {
    AppManagerConfiguration config =
        ServiceReferenceHolder.getInstance().getAPIManagerConfiguration();
    String backendServerURL = config.getFirstProperty(AppMConstants.AUTH_MANAGER_URL);

    SAML2SSOAuthenticationServiceStub stub = null;
    try {
      stub =
          new SAML2SSOAuthenticationServiceStub(
              null, backendServerURL + "/services/SAML2SSOAuthenticationService");
      AuthnReqDTO authnReqDTO = new AuthnReqDTO();
      authnReqDTO.setResponse(samlResponse);
      boolean loggedIn = stub.login(authnReqDTO);
      String cookie;
      if (loggedIn) {
        cookie =
            (String)
                stub._getServiceClient()
                    .getServiceContext()
                    .getProperty(HTTPConstants.COOKIE_STRING);
        if (log.isDebugEnabled()) {
          log.debug(
              "IdP authenticated cookie successfully retrieved via SAML2SSOAuthenticationService");
        }
        return cookie;
      } else {
        String errorMessage =
            "Login failed to IdP while tying to get the authenticated cookie via "
                + "SAML2SSOAuthenticationService";
        GatewayUtils.logAndThrowException(log, errorMessage, null);
      }
    } catch (RemoteException e) {
      String errorMessage = "Backend Server Remote Exception while initializing service";
      GatewayUtils.logAndThrowException(log, errorMessage, e);
    } finally {
      if (stub != null) {
        try {
          stub.cleanup();
        } catch (RemoteException e) {
          log.error("Error while cleaning up SAML2SSOAuthenticationServiceStub", e);
        }
      }
    }
    return null;
  }
  private Session getSession(MessageContext messageContext) {

    org.apache.axis2.context.MessageContext axis2MessageContext =
        ((Axis2MessageContext) messageContext).getAxis2MessageContext();
    Map<String, Object> headers =
        (Map<String, Object>)
            axis2MessageContext.getProperty(
                org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);

    String cookieHeaderValue = (String) headers.get(HTTPConstants.HEADER_COOKIE);

    if (cookieHeaderValue != null) {

      Map<String, String> cookies = parseRequestCookieHeader(cookieHeaderValue);

      if (cookies.get(AppMConstants.APPM_SAML2_COOKIE) != null) {
        if (log.isDebugEnabled()) {
          GatewayUtils.logWithRequestInfo(
              log,
              messageContext,
              String.format(
                  "Cookie '%s' is available in the request.", AppMConstants.APPM_SAML2_COOKIE));
        }
        messageContext.setProperty(
            AppMConstants.APPM_SAML2_COOKIE, cookies.get(AppMConstants.APPM_SAML2_COOKIE));
      } else {
        if (log.isDebugEnabled()) {
          GatewayUtils.logWithRequestInfo(
              log,
              messageContext,
              String.format(
                  "Cookie '%s' is not available in the request.", AppMConstants.APPM_SAML2_COOKIE));
        }
      }
    }

    return GatewayUtils.getSession(messageContext, true);
  }
  private void setSessionCookie(MessageContext messageContext, String cookieValue) {

    String setCookieString =
        String.format(SET_COOKIE_PATTERN, AppMConstants.APPM_SAML2_COOKIE, cookieValue, "/");

    addTransportHeader(messageContext, HTTPConstants.HEADER_SET_COOKIE, setCookieString);

    if (log.isDebugEnabled()) {
      GatewayUtils.logWithRequestInfo(
          log,
          messageContext,
          String.format(
              "Cookie '%s' has been set in the response", AppMConstants.APPM_SAML2_COOKIE));
    }
  }
  @Override
  public boolean handleRequest(MessageContext messageContext) {

    Session session = getSession(messageContext);

    // Get and set relevant message context properties.
    org.apache.axis2.context.MessageContext axis2MessageContext =
        ((Axis2MessageContext) messageContext).getAxis2MessageContext();

    String webAppContext = (String) messageContext.getProperty(RESTConstants.REST_API_CONTEXT);
    String webAppVersion =
        (String) messageContext.getProperty(RESTConstants.SYNAPSE_REST_API_VERSION);
    String fullResourceURL =
        (String) messageContext.getProperty(RESTConstants.REST_FULL_REQUEST_PATH);

    // If the request has come through the default App (the Synapse API without a version),
    // remove the version from the request URL when doing a redirection.
    String redirectionFriendlyFullRequestPath = getRedirectionReadyFullRequestPath(messageContext);
    messageContext.setProperty(
        AppMConstants.MESSAGE_CONTEXT_PROPERTY_REDIRECTION_FRIENDLY_FULL_REQUEST_PATH,
        redirectionFriendlyFullRequestPath);

    String baseURL = String.format("%s/%s/", webAppContext, webAppVersion);
    String relativeResourceURL = StringUtils.substringAfter(fullResourceURL, baseURL);
    String httpVerb = (String) axis2MessageContext.getProperty(Constants.Configuration.HTTP_METHOD);

    // Fetch the web app for the requested context and version.
    try {
      if (webApp == null) {
        int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
        webApp =
            new DefaultAppRepository(null)
                .getWebAppByContextAndVersion(webAppContext, webAppVersion, tenantId);
      }
    } catch (AppManagementException e) {
      String errorMessage =
          String.format("Can't fetch the web for '%s' from the repository.", fullResourceURL);
      GatewayUtils.logAndThrowException(log, errorMessage, e);
    }

    messageContext.setProperty(
        AppMConstants.MESSAGE_CONTEXT_PROPERTY_APP_ID, webApp.getDatabaseId());

    // Find a matched URI template.
    URITemplate matchedTemplate =
        GatewayUtils.findMatchedURITemplate(webApp, httpVerb, relativeResourceURL);
    messageContext.setProperty(
        AppMConstants.MESSAGE_CONTEXT_PROPERTY_MATCHED_URI_TEMPLATE, matchedTemplate);

    // If the request comes to the ACS URL, then it should be a SAML response or a request from the
    // IDP.
    if (isACSURL(relativeResourceURL)) {
      handleRequestToACSEndpoint(messageContext, session);

      // All requests to the ACS URL should be redirected to somewhere. e.g. IDP, app root URL
      return false;
    }

    // Handle logout requests. These requests don't need to  be authenticated.
    if (GatewayUtils.isLogoutURL(webApp, relativeResourceURL)) {
      doLogout(session);
      redirectToIDPWithLogoutRequest(messageContext, session);
      return false;
    }

    if (GatewayUtils.isAnonymousAccessAllowed(webApp, matchedTemplate)) {

      if (log.isDebugEnabled()) {
        GatewayUtils.logWithRequestInfo(
            log,
            messageContext,
            String.format("Request to '%s' is allowed for anonymous access", fullResourceURL));
      }

      messageContext.setProperty(
          AppMConstants.MESSAGE_CONTEXT_PROPERTY_GATEWAY_SKIP_SECURITY, true);
      return true;
    }

    AuthenticationContext authenticationContext = session.getAuthenticationContext();

    if (!authenticationContext.isAuthenticated()) {

      if (log.isDebugEnabled()) {
        GatewayUtils.logWithRequestInfo(
            log,
            messageContext,
            String.format("Request to '%s' is not authenticated", fullResourceURL));
      }

      session.setRequestedURL(redirectionFriendlyFullRequestPath);
      SessionStore.getInstance().updateSession(session);
      setSessionCookie(messageContext, session.getUuid());
      requestAuthentication(messageContext);
      return false;
    } else {

      if (log.isDebugEnabled()) {
        GatewayUtils.logWithRequestInfo(
            log,
            messageContext,
            String.format(
                "Request to '%s' is authenticated. Subject = '%s'",
                fullResourceURL, authenticationContext.getSubject()));
      }

      // If this web app has not been access before in this session, redirect to the IDP.
      // This is done to make sure SLO works.

      if (!session.hasAppBeenAccessedBefore(webApp.getUUID())) {
        GatewayUtils.logWithRequestInfo(
            log,
            messageContext,
            "This web app has not been accessed before in the current session. Doing SSO through IDP since it is needed to make SLO work.");
        session.setRequestedURL(redirectionFriendlyFullRequestPath);
        SessionStore.getInstance().updateSession(session);
        requestAuthentication(messageContext);
        return false;
      }

      // Set the session as a message context property.
      messageContext.setProperty(AppMConstants.APPM_SAML2_COOKIE, session.getUuid());

      if (shouldSendSAMLResponseToBackend()) {

        Map<String, String> samlResponses =
            (Map<String, String>)
                session.getAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES);

        String samlResponseForApp = null;

        if (samlResponses != null
            && (samlResponseForApp = samlResponses.get(webApp.getUUID())) != null) {

          addTransportHeader(messageContext, HTTP_HEADER_SAML_RESPONSE, samlResponseForApp);

          if (log.isDebugEnabled()) {
            GatewayUtils.logWithRequestInfo(
                log, messageContext, "SAML response has been set in the request to the backend.");
          }

        } else {

          if (log.isDebugEnabled()) {
            GatewayUtils.logWithRequestInfo(
                log, messageContext, "Couldn't find the SAML response for the app in the session.");
          }
        }
      }

      if (isJWTEnabled()) {

        String jwtHeaderName =
            configuration.getFirstProperty(APISecurityConstants.API_SECURITY_CONTEXT_HEADER);
        Map<String, String> generatedJWTs =
            (Map<String, String>) session.getAttribute(SESSION_ATTRIBUTE_JWTS);

        String jwtForApp = null;
        if (generatedJWTs != null && (jwtForApp = generatedJWTs.get(webApp.getUUID())) != null) {
          addTransportHeader(messageContext, jwtHeaderName, jwtForApp);
          if (log.isDebugEnabled()) {
            GatewayUtils.logWithRequestInfo(
                log, messageContext, "JWT has been set in the request to the backend.");
          }
        } else {
          if (log.isDebugEnabled()) {
            GatewayUtils.logWithRequestInfo(
                log, messageContext, "Couldn't find the generated JWT for the app in the session.");
          }
        }
      }

      return true;
    }
  }
 private void requestAuthentication(MessageContext messageContext) {
   AuthnRequest authenticationRequest =
       SAMLUtils.buildAuthenticationRequest(messageContext, webApp);
   GatewayUtils.redirectToIDPWithSAMLRequest(messageContext, authenticationRequest);
 }
 private void redirectToIDPWithLogoutRequest(MessageContext messageContext, Session session) {
   LogoutRequest logoutRequest = SAMLUtils.buildLogoutRequest(webApp.getSaml2SsoIssuer(), session);
   GatewayUtils.logWithRequestInfo(log, messageContext, "Redirecting to the IDP for logging out.");
   GatewayUtils.redirectToIDPWithSAMLRequest(messageContext, logoutRequest);
 }
  private boolean handleRequestToACSEndpoint(MessageContext messageContext, Session session) {

    String fullResourceURL =
        (String) messageContext.getProperty(RESTConstants.REST_FULL_REQUEST_PATH);

    // Build the incoming message.
    GatewayUtils.buildIncomingMessage(messageContext);

    IDPMessage idpMessage = null;
    try {
      idpMessage = SAMLUtils.processIDPMessage(messageContext);

      if (idpMessage.getSAMLResponse() == null && idpMessage.getSAMLRequest() == null) {
        String errorMessage =
            String.format(
                "A SAML request or response was not there in the request to the ACS URL ('%s')",
                fullResourceURL);
        GatewayUtils.logAndThrowException(log, errorMessage, null);
      }

      if (idpMessage.getRawSAMLResponse() != null) {
        // pass saml response and request for an authorized cookie to access admin services
        String authorizedAdminCookie =
            getAuthenticatedCookieFromIdP(idpMessage.getRawSAMLResponse());
        if (authorizedAdminCookie == null) {
          String errorMessage =
              "Error while requesting the authorized cookie to access IDP admin services via "
                  + "SAML2SSOAuthenticationService";
          GatewayUtils.logAndThrowException(log, errorMessage, null);
        }
        // add to session
        if (session.getAttribute(AppMConstants.IDP_AUTHENTICATED_COOKIE) == null) {
          session.addAttribute(AppMConstants.IDP_AUTHENTICATED_COOKIE, authorizedAdminCookie);
          SessionStore.getInstance().updateSession(session);
        }
      }
    } catch (SAMLException e) {
      String errorMessage =
          String.format(
              "Error while processing the IDP call back request to the ACS URL ('%s')",
              fullResourceURL);
      log.error(errorMessage);
      if (log.isDebugEnabled()) { // Do not log the stack trace without checking isDebugEnabled,
        // because log can be filled by SAML Response XSW attacks.
        log.debug(errorMessage, e);
      }
      GatewayUtils.send401(messageContext, "Unauthorized SAML Response");
      return false;
    }

    GatewayUtils.logWithRequestInfo(
        log,
        messageContext,
        String.format(
            "%s is available in request.",
            idpMessage.getSAMLRequest() != null ? "SAMLRequest" : "SAMLResponse"));

    // If not configured, the SLO request URL and the SLO response URL is the ACS URL by default.
    // SLOResponse is handled in this method. But the SLORequest is handled generically in the rest
    // of the handler code.
    if (idpMessage.isSLOResponse()) {
      GatewayUtils.logWithRequestInfo(log, messageContext, "SAMLResponse in an SLO response.");
      GatewayUtils.redirectToURL(messageContext, GatewayUtils.getAppRootURL(messageContext));
      return false;
    } else if (idpMessage.isSLORequest()) {
      GatewayUtils.logWithRequestInfo(log, messageContext, "SAMLRequest in an SLO request.");
      OMElement response =
          handleSLORequest(messageContext, (LogoutRequest) idpMessage.getSAMLRequest());
      GatewayUtils.send200(messageContext, response);
      return false;
    } else {

      // Validate SAML response validity period
      if (!idpMessage.validateAssertionValidityPeriod()) {
        requestAuthentication(messageContext);
        return false;
      }

      // Validate SAML response signature, assertion signature and audience restrictions
      if (!idpMessage.validateSignatureAndAudienceRestriction(
          idpMessage.getSAMLResponse(), webApp, configuration)) {
        GatewayUtils.send401(messageContext, "Unauthorized SAML Response");
        return false;
      }

      AuthenticationContext authenticationContext =
          getAuthenticationContextFromIDPCallback(idpMessage);

      if (authenticationContext.isAuthenticated()) {;

        if (log.isDebugEnabled()) {
          GatewayUtils.logWithRequestInfo(
              log,
              messageContext,
              String.format(
                  "SAML response is authenticated. Subject = '%s'",
                  authenticationContext.getSubject()));
        }

        session.setAuthenticationContext(authenticationContext);

        if (shouldSendSAMLResponseToBackend()) {

          Map<String, String> samlResponses =
              (Map<String, String>)
                  session.getAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES);

          if (samlResponses == null) {
            samlResponses = new HashMap<String, String>();
            session.addAttribute(SAMLUtils.SESSION_ATTRIBUTE_RAW_SAML_RESPONSES, samlResponses);
          }

          samlResponses.put(webApp.getUUID(), idpMessage.getRawSAMLResponse());
        }

        // Get the SAML session index.
        String sessionIndex =
            (String) SAMLUtils.getSessionIndex((ResponseImpl) idpMessage.getSAMLResponse());
        session.addAttribute(SAMLUtils.SESSION_ATTRIBUTE_SAML_SESSION_INDEX, sessionIndex);
        GatewayUtils.logWithRequestInfo(
            log, messageContext, String.format("Session index : %s", sessionIndex));

        // Add Session Index -> Session ID to a cache.
        CacheManager.getInstance()
            .getSessionIndexMappingCache()
            .put(sessionIndex, session.getUuid());

        // Mark this web app as an access web app in this session.
        session.addAccessedWebAppUUID(webApp.getUUID());

        Map<String, Object> userAttributes =
            getUserAttributes((ResponseImpl) idpMessage.getSAMLResponse());
        session.getAuthenticationContext().setAttributes(userAttributes);

        String roleAttributeValue = (String) userAttributes.get("http://wso2.org/claims/role");

        if (roleAttributeValue != null) {
          String[] roles = roleAttributeValue.split(",");
          for (String role : roles) {
            session.getAuthenticationContext().addRole(role);
          }
        }

        // Generate the JWT and store in the session.
        if (isJWTEnabled()) {
          try {

            // Generate and store the JWT against the app.
            Map<String, String> generatedJWTs =
                (Map<String, String>) session.getAttribute(SESSION_ATTRIBUTE_JWTS);

            if (generatedJWTs == null) {
              generatedJWTs = new HashMap<String, String>();
              session.addAttribute(SESSION_ATTRIBUTE_JWTS, generatedJWTs);
            }

            generatedJWTs.put(
                webApp.getUUID(),
                getJWTGenerator().generateToken(userAttributes, webApp, messageContext));

          } catch (AppManagementException e) {
            String errorMessage =
                String.format(
                    "Can't generate JWT for the subject : '%s'",
                    authenticationContext.getSubject());
            GatewayUtils.logAndThrowException(log, errorMessage, e);
          }
        }

        SessionStore.getInstance().updateSession(session);
        if (session.getRequestedURL() != null) {
          GatewayUtils.redirectToURL(messageContext, session.getRequestedURL());
        } else {
          log.warn(
              String.format(
                  "Original requested URL in the session is null. Redirecting to the app root URL."));
          GatewayUtils.redirectToURL(messageContext, GatewayUtils.getAppRootURL(messageContext));
        }

        return false;
      } else {

        if (log.isDebugEnabled()) {
          GatewayUtils.logWithRequestInfo(
              log, messageContext, "SAML response is not authenticated.");
        }

        requestAuthentication(messageContext);
        return false;
      }
    }
  }