/**
  * Builds the SAML error response and sets the compressed value to the reqValidationResponseDTO
  *
  * @param id
  * @param status
  * @param statMsg
  * @param destination
  * @return
  * @throws IdentityException
  */
 private SAMLSSOReqValidationResponseDTO buildErrorResponse(
     String id,
     String status,
     String statMsg,
     String destination,
     String responseSigningAlgorithmUri,
     String responseDigestAlgorithmUri)
     throws IdentityException {
   SAMLSSOReqValidationResponseDTO reqValidationResponseDTO =
       new SAMLSSOReqValidationResponseDTO();
   LogoutResponse logoutResp =
       new SingleLogoutMessageBuilder()
           .buildLogoutResponse(
               id,
               status,
               statMsg,
               destination,
               false,
               null,
               responseSigningAlgorithmUri,
               responseDigestAlgorithmUri);
   reqValidationResponseDTO.setLogOutReq(true);
   reqValidationResponseDTO.setValid(false);
   try {
     reqValidationResponseDTO.setResponse(
         SAMLSSOUtil.compressResponse(SAMLSSOUtil.marshall(logoutResp)));
   } catch (IOException e) {
     throw new IdentityException("Error while creating logout response", e);
   }
   return reqValidationResponseDTO;
 }
  /**
   * If the SAMLRequest is a Logout request then IDP will send logout requests to other session
   * participants and then sends the logout Response back to the initiator. In case of
   * authentication request, check if there is a valid session for the user, if there is, the user
   * will be redirected directly to the Service Provider, if not the user will be redirected to the
   * login page.
   *
   * @param req
   * @param resp
   * @param sessionId
   * @param samlRequest
   * @param relayState
   * @param authMode
   * @throws IdentityException
   * @throws IOException
   * @throws ServletException
   * @throws org.wso2.carbon.identity.base.IdentityException
   */
  private void handleSPInitSSO(
      HttpServletRequest req,
      HttpServletResponse resp,
      String queryString,
      String relayState,
      String authMode,
      String samlRequest,
      String sessionId,
      boolean isPost)
      throws UserStoreException, IdentityException, IOException, ServletException {

    String rpSessionId =
        CharacterEncoder.getSafeText(req.getParameter(MultitenantConstants.SSO_AUTH_SESSION_ID));
    SAMLSSOService samlSSOService = new SAMLSSOService();

    SAMLSSOReqValidationResponseDTO signInRespDTO =
        samlSSOService.validateSPInitSSORequest(
            samlRequest, queryString, sessionId, rpSessionId, authMode, isPost);

    if (!signInRespDTO.isLogOutReq()) { // an <AuthnRequest> received
      if (signInRespDTO.isValid()) {
        sendToFrameworkForAuthentication(req, resp, signInRespDTO, relayState, isPost);
      } else {
        // TODO send invalid response to SP
        if (log.isDebugEnabled()) {
          log.debug("Invalid SAML SSO Request : " + samlRequest);
        }
        String errorResp = signInRespDTO.getResponse();
        sendNotification(
            errorResp,
            SAMLSSOConstants.Notification.EXCEPTION_STATUS,
            SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
            signInRespDTO.getAssertionConsumerURL(),
            req,
            resp);
      }
    } else { // a <LogoutRequest> received
      if (signInRespDTO.isValid()) {
        sendToFrameworkForLogout(req, resp, signInRespDTO, relayState, sessionId, false, isPost);
      } else {
        if (log.isDebugEnabled()) {
          log.debug("Invalid SAML SSO Logout Request : " + samlRequest);
        }
        if (signInRespDTO.isLogoutFromAuthFramework()) {
          sendToFrameworkForLogout(req, resp, null, null, sessionId, true, isPost);
        } else {
          // TODO send invalid response to SP
          String errorResp = signInRespDTO.getResponse();
          sendNotification(
              errorResp,
              SAMLSSOConstants.Notification.EXCEPTION_STATUS,
              SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
              signInRespDTO.getAssertionConsumerURL(),
              req,
              resp);
        }
      }
    }
  }
  /**
   * Validates the authentication request according to IdP Initiated SAML SSO Web Browser
   * Specification
   *
   * @return SAMLSSOSignInResponseDTO
   * @throws org.wso2.carbon.identity.base.IdentityException
   */
  public SAMLSSOReqValidationResponseDTO validate() throws IdentityException {

    SAMLSSOReqValidationResponseDTO validationResponse = new SAMLSSOReqValidationResponseDTO();
    try {

      // spEntityID MUST NOT be null
      if (StringUtils.isNotBlank(spEntityID)) {
        validationResponse.setIssuer(spEntityID);
      } else {
        String errorResp =
            SAMLSSOUtil.buildErrorResponse(
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                "spEntityID parameter not found in request",
                null);
        if (log.isDebugEnabled()) {
          log.debug("spEntityID parameter not found in request");
        }
        validationResponse.setResponse(errorResp);
        validationResponse.setValid(false);
        return validationResponse;
      }

      if (!SAMLSSOUtil.isSAMLIssuerExists(
          spEntityID, SAMLSSOUtil.getTenantDomainFromThreadLocal())) {
        String message =
            "A Service Provider with the Issuer '"
                + spEntityID
                + "' is not registered. Service "
                + "Provider should be registered in advance";
        log.error(message);
        String errorResp =
            SAMLSSOUtil.buildErrorResponse(
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, null);
        validationResponse.setResponse(errorResp);
        validationResponse.setValid(false);
        return validationResponse;
      }

      // If SP has multiple ACS
      if (StringUtils.isNotBlank(acs)) {
        validationResponse.setAssertionConsumerURL(acs);
      }

      if (StringUtils.isBlank(SAMLSSOUtil.getTenantDomainFromThreadLocal())) {
        SAMLSSOUtil.setTenantDomainInThreadLocal(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
      }

      validationResponse.setValid(true);

      if (log.isDebugEnabled()) {
        log.debug("IdP Initiated SSO request validation is successful");
      }
      return validationResponse;
    } catch (Exception e) {
      throw IdentityException.error("Error validating the IdP Initiated SSO request", e);
    }
  }
  private void handleLogoutResponseFromFramework(
      HttpServletRequest request, HttpServletResponse response, SAMLSSOSessionDTO sessionDTO)
      throws ServletException, IOException, IdentityException {

    SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO();

    if (validationResponseDTO != null) {
      // sending LogoutRequests to other session participants
      LogoutRequestSender.getInstance()
          .sendLogoutRequests(validationResponseDTO.getLogoutRespDTO());
      SAMLSSOUtil.removeSession(sessionDTO.getSessionId(), validationResponseDTO.getIssuer());
      removeSessionDataFromCache(
          CharacterEncoder.getSafeText(request.getParameter("sessionDataKey")));

      if (validationResponseDTO.isIdPInitSLO()) {
        // redirecting to the return URL or IS logout page
        response.sendRedirect(validationResponseDTO.getReturnToURL());
      } else {
        // sending LogoutResponse back to the initiator
        sendResponse(
            request,
            response,
            sessionDTO.getRelayState(),
            validationResponseDTO.getLogoutResponse(),
            validationResponseDTO.getAssertionConsumerURL(),
            validationResponseDTO.getSubject(),
            null,
            sessionDTO.getTenantDomain());
      }
    } else {
      try {
        samlSsoService.doSingleLogout(request.getSession().getId());
      } catch (IdentityException e) {
        log.error("Error when processing the logout request!", e);
      }

      String errorResp =
          SAMLSSOUtil.buildErrorResponse(
              SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
              "Invalid request",
              sessionDTO.getAssertionConsumerURL());
      sendNotification(
          errorResp,
          SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS,
          SAMLSSOConstants.Notification.INVALID_MESSAGE_MESSAGE,
          sessionDTO.getAssertionConsumerURL(),
          request,
          response);
    }
  }
  /**
   * @param logoutRequest
   * @param sessionId
   * @param queryString
   * @return
   * @throws IdentityException
   */
  public SAMLSSOReqValidationResponseDTO process(
      LogoutRequest logoutRequest, String sessionId, String queryString) throws IdentityException {

    try {
      SAMLSSOReqValidationResponseDTO reqValidationResponseDTO =
          new SAMLSSOReqValidationResponseDTO();
      reqValidationResponseDTO.setLogOutReq(true);

      String subject = null;
      String issuer = null;
      String defaultSigningAlgoUri = IdentityApplicationManagementUtil.getSigningAlgoURIByConfig();
      String defaultDigestAlgoUri = IdentityApplicationManagementUtil.getDigestAlgoURIByConfig();

      // Get the sessions from the SessionPersistenceManager and prepare
      // the logout responses
      SSOSessionPersistenceManager ssoSessionPersistenceManager =
          SSOSessionPersistenceManager.getPersistenceManager();
      if (StringUtils.isBlank(sessionId)) {
        String message = "Session was already Expired";
        log.error(message);
        return buildErrorResponse(
            logoutRequest.getID(),
            SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
            message,
            logoutRequest.getDestination(),
            defaultSigningAlgoUri,
            defaultDigestAlgoUri);
      }
      String sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId);

      if (StringUtils.isBlank(sessionId)) {
        String message = "Session index value not found in the request";
        log.error(message);
        reqValidationResponseDTO =
            buildErrorResponse(
                logoutRequest.getID(),
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                message,
                null,
                defaultSigningAlgoUri,
                defaultDigestAlgoUri);
        reqValidationResponseDTO.setLogoutFromAuthFramework(true);
        return reqValidationResponseDTO;
      }
      // Only if the logout request is received.
      if (logoutRequest != null) {
        if (logoutRequest.getIssuer() == null) {
          String message = "Issuer should be mentioned in the Logout Request";
          log.error(message);
          return buildErrorResponse(
              logoutRequest.getID(),
              SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
              message,
              logoutRequest.getDestination(),
              defaultSigningAlgoUri,
              defaultDigestAlgoUri);
        }

        // TODO : Check for BaseID and EncryptedID as well.
        if (logoutRequest.getNameID() != null) {
          NameID nameID = logoutRequest.getNameID();
          subject = nameID.getValue();
        } else {
          String message = "Subject Name should be specified in the Logout Request";
          log.error(message);
          return buildErrorResponse(
              logoutRequest.getID(),
              SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
              message,
              logoutRequest.getDestination(),
              defaultSigningAlgoUri,
              defaultDigestAlgoUri);
        }

        if (logoutRequest.getSessionIndexes() == null) {
          String message = "At least one Session Index should be present in the Logout Request";
          log.error(message);
          return buildErrorResponse(
              logoutRequest.getID(),
              SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
              message,
              logoutRequest.getDestination(),
              defaultSigningAlgoUri,
              defaultDigestAlgoUri);
        }

        SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex);

        if (sessionInfoData == null) {
          String message = "No Established Sessions corresponding to Session Indexes provided.";
          log.error(message);
          reqValidationResponseDTO =
              buildErrorResponse(
                  logoutRequest.getID(),
                  SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                  message,
                  null,
                  defaultSigningAlgoUri,
                  defaultDigestAlgoUri);
          reqValidationResponseDTO.setLogoutFromAuthFramework(true);
          return reqValidationResponseDTO;
        }

        issuer = logoutRequest.getIssuer().getValue();

        if (issuer.contains("@")) {
          String tenantDomain = issuer.substring(issuer.lastIndexOf('@') + 1);
          issuer = issuer.substring(0, issuer.lastIndexOf('@'));
          if (StringUtils.isNotEmpty(tenantDomain) && StringUtils.isNotEmpty(issuer)) {
            SAMLSSOUtil.setTenantDomainInThreadLocal(tenantDomain);
            if (log.isDebugEnabled()) {
              log.debug(
                  "Tenant Domain :"
                      + " "
                      + tenantDomain
                      + " "
                      + "&"
                      + " "
                      + "Issuer name :"
                      + issuer
                      + " "
                      + "has being spilt");
            }
          } else {
            SAMLSSOServiceProviderDO serviceProvider =
                sessionInfoData.getServiceProviderList().get(issuer);
            if (serviceProvider != null) {
              SAMLSSOUtil.setTenantDomainInThreadLocal(
                  sessionInfoData.getServiceProviderList().get(issuer).getTenantDomain());
            } else {
              throw new IdentityException(
                  "Service provider :" + issuer + " does not exist in session " + "info data.");
            }
          }
        } else {
          SAMLSSOServiceProviderDO serviceProvider =
              sessionInfoData.getServiceProviderList().get(issuer);
          if (serviceProvider != null) {
            SAMLSSOUtil.setTenantDomainInThreadLocal(
                sessionInfoData.getServiceProviderList().get(issuer).getTenantDomain());
          } else {
            throw new IdentityException(
                "Service provider :" + issuer + " does not exist in session info " + "data.");
          }
        }
        subject = sessionInfoData.getSubject(issuer);

        Map<String, SAMLSSOServiceProviderDO> sessionsList =
            sessionInfoData.getServiceProviderList();
        SAMLSSOServiceProviderDO logoutReqIssuer = sessionsList.get(issuer);

        if (logoutReqIssuer.isDoSingleLogout()) {
          // validate session index
          SessionIndex requestSessionIndex =
              logoutRequest.getSessionIndexes().size() > 0
                  ? logoutRequest.getSessionIndexes().get(0)
                  : null;

          if (requestSessionIndex == null
              || !sessionIndex.equals(requestSessionIndex.getSessionIndex())) {
            String message =
                "Session Index validation for Logout Request failed. "
                    + "Received: ["
                    + (requestSessionIndex == null ? "null" : requestSessionIndex.getSessionIndex())
                    + "]."
                    + " Expected: ["
                    + sessionIndex
                    + "]";
            log.error(message);
            return buildErrorResponse(
                logoutRequest.getID(),
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                message,
                logoutRequest.getDestination(),
                logoutReqIssuer.getSigningAlgorithmUri(),
                logoutReqIssuer.getDigestAlgorithmUri());
          }
        }

        if (logoutReqIssuer.isDoValidateSignatureInRequests()) {

          // Validate 'Destination'
          List<String> idpUrlSet =
              SAMLSSOUtil.getDestinationFromTenantDomain(
                  SAMLSSOUtil.getTenantDomainFromThreadLocal());

          if (logoutRequest.getDestination() == null
              || !idpUrlSet.contains(logoutRequest.getDestination())) {
            String message =
                "Destination validation for Logout Request failed. "
                    + "Received: ["
                    + logoutRequest.getDestination()
                    + "]."
                    + " Expected: ["
                    + StringUtils.join(idpUrlSet, ',')
                    + "]";
            log.error(message);
            return buildErrorResponse(
                logoutRequest.getID(),
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                message,
                logoutRequest.getDestination(),
                logoutReqIssuer.getSigningAlgorithmUri(),
                logoutReqIssuer.getDigestAlgorithmUri());
          }

          // Validate Signature
          boolean isSignatureValid =
              SAMLSSOUtil.validateLogoutRequestSignature(
                  logoutRequest, logoutReqIssuer.getCertAlias(), subject, queryString);
          if (!isSignatureValid) {
            String message = "Signature validation for Logout Request failed";
            log.error(message);
            return buildErrorResponse(
                logoutRequest.getID(),
                SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR,
                message,
                logoutRequest.getDestination(),
                logoutReqIssuer.getSigningAlgorithmUri(),
                logoutReqIssuer.getDigestAlgorithmUri());
          }
        }

        SingleLogoutMessageBuilder logoutMsgBuilder = new SingleLogoutMessageBuilder();
        Map<String, String> rpSessionsList = sessionInfoData.getRPSessionsList();
        List<SingleLogoutRequestDTO> singleLogoutReqDTOs = new ArrayList<SingleLogoutRequestDTO>();

        for (Map.Entry<String, SAMLSSOServiceProviderDO> entry : sessionsList.entrySet()) {
          String key = entry.getKey();
          SAMLSSOServiceProviderDO value = entry.getValue();

          if (!key.equals(issuer)) {
            SingleLogoutRequestDTO logoutReqDTO = new SingleLogoutRequestDTO();
            if (StringUtils.isNotBlank(value.getSloRequestURL())) {
              logoutReqDTO.setAssertionConsumerURL(value.getSloRequestURL());
            } else if (StringUtils.isNotBlank(value.getSloResponseURL())) {
              logoutReqDTO.setAssertionConsumerURL(value.getSloResponseURL());
            } else {
              logoutReqDTO.setAssertionConsumerURL(value.getAssertionConsumerUrl());
            }

            LogoutRequest logoutReq =
                logoutMsgBuilder.buildLogoutRequest(
                    sessionInfoData.getSubject(key),
                    sessionIndex,
                    SAMLSSOConstants.SingleLogoutCodes.LOGOUT_USER,
                    logoutReqDTO.getAssertionConsumerURL(),
                    value.getNameIDFormat(),
                    value.getTenantDomain(),
                    value.getSigningAlgorithmUri(),
                    value.getDigestAlgorithmUri());
            String logoutReqString = SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutReq));
            logoutReqDTO.setLogoutResponse(logoutReqString);
            logoutReqDTO.setRpSessionId(rpSessionsList.get(key));
            singleLogoutReqDTOs.add(logoutReqDTO);
          } else {
            reqValidationResponseDTO.setIssuer(value.getIssuer());
            reqValidationResponseDTO.setDoSignResponse(value.isDoSignResponse());
            reqValidationResponseDTO.setSigningAlgorithmUri(value.getSigningAlgorithmUri());
            reqValidationResponseDTO.setDigestAlgorithmUri(value.getDigestAlgorithmUri());
            if (StringUtils.isNotBlank(value.getSloResponseURL())) {
              reqValidationResponseDTO.setAssertionConsumerURL(value.getSloResponseURL());
            } else {
              reqValidationResponseDTO.setAssertionConsumerURL(value.getAssertionConsumerUrl());
            }
          }
        }

        reqValidationResponseDTO.setLogoutRespDTO(
            singleLogoutReqDTOs.toArray(new SingleLogoutRequestDTO[singleLogoutReqDTOs.size()]));

        LogoutResponse logoutResponse =
            logoutMsgBuilder.buildLogoutResponse(
                logoutRequest.getID(),
                SAMLSSOConstants.StatusCodes.SUCCESS_CODE,
                null,
                reqValidationResponseDTO.getAssertionConsumerURL(),
                reqValidationResponseDTO.isDoSignResponse(),
                SAMLSSOUtil.getTenantDomainFromThreadLocal(),
                reqValidationResponseDTO.getSigningAlgorithmUri(),
                reqValidationResponseDTO.getDigestAlgorithmUri());

        reqValidationResponseDTO.setLogoutResponse(
            SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse)));
        reqValidationResponseDTO.setValid(true);
      }

      return reqValidationResponseDTO;
    } catch (UserStoreException | IdentityException e) {
      throw new IdentityException("Error Processing the Logout Request", e);
    }
  }
  /**
   * This method handles authentication and sends authentication Response message back to the
   * Service Provider after successful authentication. In case of authentication failure the user is
   * prompted back for authentication.
   *
   * @param req
   * @param resp
   * @param sessionId
   * @throws IdentityException
   * @throws IOException
   * @throws ServletException
   */
  private void handleAuthenticationReponseFromFramework(
      HttpServletRequest req,
      HttpServletResponse resp,
      String sessionId,
      SAMLSSOSessionDTO sessionDTO)
      throws UserStoreException, IdentityException, IOException, ServletException {

    String sessionDataKey = CharacterEncoder.getSafeText(req.getParameter("sessionDataKey"));
    AuthenticationResult authResult = getAuthenticationResultFromCache(sessionDataKey);

    if (log.isDebugEnabled() && authResult == null) {
      log.debug("Session data is not found for key : " + sessionDataKey);
    }
    SAMLSSOReqValidationResponseDTO reqValidationDTO = sessionDTO.getValidationRespDTO();
    SAMLSSOAuthnReqDTO authnReqDTO = new SAMLSSOAuthnReqDTO();

    if (authResult == null || !authResult.isAuthenticated()) {

      if (log.isDebugEnabled() && authResult != null) {
        log.debug("Unauthenticated User");
      }

      if (reqValidationDTO.isPassive()) { // if passive

        List<String> statusCodes = new ArrayList<String>();
        statusCodes.add(SAMLSSOConstants.StatusCodes.NO_PASSIVE);
        statusCodes.add(SAMLSSOConstants.StatusCodes.IDENTITY_PROVIDER_ERROR);
        String destination = reqValidationDTO.getDestination();
        reqValidationDTO.setResponse(
            SAMLSSOUtil.buildErrorResponse(
                reqValidationDTO.getId(),
                statusCodes,
                "Cannot authenticate Subject in Passive Mode",
                destination));

        sendResponse(
            req,
            resp,
            sessionDTO.getRelayState(),
            reqValidationDTO.getResponse(),
            reqValidationDTO.getAssertionConsumerURL(),
            reqValidationDTO.getSubject(),
            null,
            sessionDTO.getTenantDomain());
        return;

      } else { // if forceAuthn or normal flow
        // TODO send a saml response with a status message.
        if (!authResult.isAuthenticated()) {
          String destination = reqValidationDTO.getDestination();
          String errorResp =
              SAMLSSOUtil.buildErrorResponse(
                  SAMLSSOConstants.StatusCodes.AUTHN_FAILURE,
                  "User authentication failed",
                  destination);
          sendNotification(
              errorResp,
              SAMLSSOConstants.Notification.EXCEPTION_STATUS,
              SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
              reqValidationDTO.getAssertionConsumerURL(),
              req,
              resp);
          return;
        } else {
          throw new IdentityException("Session data is not found for authenticated user");
        }
      }
    } else {
      populateAuthnReqDTO(req, authnReqDTO, sessionDTO, authResult);
      req.setAttribute(SAMLSSOConstants.AUTHENTICATION_RESULT, authResult);
      String relayState = null;

      if (req.getParameter(SAMLSSOConstants.RELAY_STATE) != null) {
        relayState = req.getParameter(SAMLSSOConstants.RELAY_STATE);
      } else {
        relayState = sessionDTO.getRelayState();
      }

      startTenantFlow(authnReqDTO.getTenantDomain());

      if (sessionId == null) {
        sessionId = UUIDGenerator.generateUUID();
      }

      SAMLSSOService samlSSOService = new SAMLSSOService();
      SAMLSSORespDTO authRespDTO =
          samlSSOService.authenticate(
              authnReqDTO,
              sessionId,
              authResult.isAuthenticated(),
              authResult.getAuthenticatedAuthenticators(),
              SAMLSSOConstants.AuthnModes.USERNAME_PASSWORD);

      if (authRespDTO.isSessionEstablished()) { // authenticated

        storeTokenIdCookie(sessionId, req, resp, authnReqDTO.getTenantDomain());
        removeSessionDataFromCache(
            CharacterEncoder.getSafeText(req.getParameter("sessionDataKey")));

        sendResponse(
            req,
            resp,
            relayState,
            authRespDTO.getRespString(),
            authRespDTO.getAssertionConsumerURL(),
            authRespDTO.getSubject().getAuthenticatedSubjectIdentifier(),
            authResult.getAuthenticatedIdPs(),
            sessionDTO.getTenantDomain());
      } else { // authentication FAILURE
        String errorResp = authRespDTO.getRespString();
        sendNotification(
            errorResp,
            SAMLSSOConstants.Notification.EXCEPTION_STATUS,
            SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
            authRespDTO.getAssertionConsumerURL(),
            req,
            resp);
      }
    }
  }
  private void sendToFrameworkForLogout(
      HttpServletRequest request,
      HttpServletResponse response,
      SAMLSSOReqValidationResponseDTO signInRespDTO,
      String relayState,
      String sessionId,
      boolean invalid,
      boolean isPost)
      throws ServletException, IOException {

    SAMLSSOSessionDTO sessionDTO = new SAMLSSOSessionDTO();
    sessionDTO.setHttpQueryString(request.getQueryString());
    sessionDTO.setRelayState(relayState);
    sessionDTO.setSessionId(sessionId);
    sessionDTO.setLogoutReq(true);
    sessionDTO.setInvalidLogout(invalid);

    if (signInRespDTO != null) {
      sessionDTO.setDestination(signInRespDTO.getDestination());
      sessionDTO.setRequestMessageString(signInRespDTO.getRequestMessageString());
      sessionDTO.setIssuer(signInRespDTO.getIssuer());
      sessionDTO.setRequestID(signInRespDTO.getId());
      sessionDTO.setSubject(signInRespDTO.getSubject());
      sessionDTO.setRelyingPartySessionId(signInRespDTO.getRpSessionId());
      sessionDTO.setAssertionConsumerURL(signInRespDTO.getAssertionConsumerURL());
      sessionDTO.setValidationRespDTO(signInRespDTO);
    }

    String sessionDataKey = UUIDGenerator.generateUUID();
    addSessionDataToCache(
        sessionDataKey,
        sessionDTO,
        IdPManagementUtil.getIdleSessionTimeOut(
            CarbonContext.getThreadLocalCarbonContext().getTenantDomain()));

    String commonAuthURL = CarbonUIUtil.getAdminConsoleURL(request);
    commonAuthURL = commonAuthURL.replace("samlsso/carbon/", "commonauth");

    String selfPath = URLEncoder.encode("/samlsso", "UTF-8");

    // Add all parameters to authentication context before sending to authentication
    // framework
    AuthenticationRequest authenticationRequest = new AuthenticationRequest();
    authenticationRequest.addRequestQueryParam(
        FrameworkConstants.RequestParams.LOGOUT, new String[] {"true"});
    authenticationRequest.setRequestQueryParams(request.getParameterMap());
    authenticationRequest.setCommonAuthCallerPath(selfPath);
    authenticationRequest.setPost(isPost);

    if (signInRespDTO != null) {
      authenticationRequest.setRelyingParty(signInRespDTO.getIssuer());
    }
    authenticationRequest.appendRequestQueryParams(request.getParameterMap());
    // Add headers to AuthenticationRequestContext
    for (Enumeration e = request.getHeaderNames(); e.hasMoreElements(); ) {
      String headerName = e.nextElement().toString();
      authenticationRequest.addHeader(headerName, request.getHeader(headerName));
    }

    AuthenticationRequestCacheEntry authRequest =
        new AuthenticationRequestCacheEntry(authenticationRequest);
    FrameworkUtils.addAuthenticationRequestToCache(
        sessionDataKey,
        authRequest,
        IdPManagementUtil.getIdleSessionTimeOut(
            CarbonContext.getThreadLocalCarbonContext().getTenantDomain()));
    String queryParams =
        "?"
            + SAMLSSOConstants.SESSION_DATA_KEY
            + "="
            + sessionDataKey
            + "&"
            + "type"
            + "="
            + "samlsso";

    response.sendRedirect(commonAuthURL + queryParams);
  }
  /**
   * Sends the user for authentication to the login page
   *
   * @param req
   * @param resp
   * @param signInRespDTO
   * @param relayState
   * @throws ServletException
   * @throws IOException
   */
  private void sendToFrameworkForAuthentication(
      HttpServletRequest req,
      HttpServletResponse resp,
      SAMLSSOReqValidationResponseDTO signInRespDTO,
      String relayState,
      boolean isPost)
      throws ServletException, IOException, UserStoreException, IdentityException {

    SAMLSSOSessionDTO sessionDTO = new SAMLSSOSessionDTO();
    sessionDTO.setHttpQueryString(req.getQueryString());
    sessionDTO.setDestination(signInRespDTO.getDestination());
    sessionDTO.setRelayState(relayState);
    sessionDTO.setRequestMessageString(signInRespDTO.getRequestMessageString());
    sessionDTO.setIssuer(signInRespDTO.getIssuer());
    sessionDTO.setRequestID(signInRespDTO.getId());
    sessionDTO.setSubject(signInRespDTO.getSubject());
    sessionDTO.setRelyingPartySessionId(signInRespDTO.getRpSessionId());
    sessionDTO.setAssertionConsumerURL(signInRespDTO.getAssertionConsumerURL());
    sessionDTO.setTenantDomain(SAMLSSOUtil.getTenantDomainFromThreadLocal());

    if (sessionDTO.getTenantDomain() == null) {
      String[] splitIssuer = sessionDTO.getIssuer().split("@");
      if (splitIssuer != null
          && splitIssuer.length == 2
          && !splitIssuer[0].trim().isEmpty()
          && !splitIssuer[1].trim().isEmpty()) {
        sessionDTO.setTenantDomain(splitIssuer[1]);
      } else {
        sessionDTO.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
      }
    }
    SAMLSSOUtil.setTenantDomainInThreadLocal(sessionDTO.getTenantDomain());

    sessionDTO.setForceAuth(signInRespDTO.isForceAuthn());
    sessionDTO.setPassiveAuth(signInRespDTO.isPassive());
    sessionDTO.setValidationRespDTO(signInRespDTO);
    sessionDTO.setIdPInitSSO(signInRespDTO.isIdPInitSSO());

    String sessionDataKey = UUIDGenerator.generateUUID();
    addSessionDataToCache(
        sessionDataKey,
        sessionDTO,
        IdPManagementUtil.getIdleSessionTimeOut(sessionDTO.getTenantDomain()));

    String commonAuthURL = CarbonUIUtil.getAdminConsoleURL(req);
    commonAuthURL =
        commonAuthURL.replace(
            FrameworkConstants.RequestType.CLAIM_TYPE_SAML_SSO
                + "/"
                + FrameworkConstants.CARBON
                + "/",
            FrameworkConstants.COMMONAUTH);
    String selfPath =
        URLEncoder.encode("/" + FrameworkConstants.RequestType.CLAIM_TYPE_SAML_SSO, "UTF-8");
    // Setting authentication request context
    AuthenticationRequest authenticationRequest = new AuthenticationRequest();

    // Adding query parameters
    authenticationRequest.appendRequestQueryParams(req.getParameterMap());
    for (Enumeration headerNames = req.getHeaderNames(); headerNames.hasMoreElements(); ) {
      String headerName = headerNames.nextElement().toString();
      authenticationRequest.addHeader(headerName, req.getHeader(headerName));
    }

    authenticationRequest.setRelyingParty(signInRespDTO.getIssuer());
    authenticationRequest.setCommonAuthCallerPath(selfPath);
    authenticationRequest.setForceAuth(signInRespDTO.isForceAuthn());
    if (!authenticationRequest.getForceAuth()
        && authenticationRequest.getRequestQueryParam("forceAuth") != null) {
      String[] forceAuth = authenticationRequest.getRequestQueryParam("forceAuth");
      if (!forceAuth[0].trim().isEmpty() && Boolean.parseBoolean(forceAuth[0].trim())) {
        authenticationRequest.setForceAuth(Boolean.parseBoolean(forceAuth[0].trim()));
      }
    }
    authenticationRequest.setPassiveAuth(signInRespDTO.isPassive());
    authenticationRequest.setTenantDomain(sessionDTO.getTenantDomain());
    authenticationRequest.setPost(isPost);

    // Creating cache entry and adding entry to the cache before calling to commonauth
    AuthenticationRequestCacheEntry authRequest =
        new AuthenticationRequestCacheEntry(authenticationRequest);
    FrameworkUtils.addAuthenticationRequestToCache(
        sessionDataKey,
        authRequest,
        IdPManagementUtil.getIdleSessionTimeOut(sessionDTO.getTenantDomain()));
    StringBuilder queryStringBuilder = new StringBuilder();
    queryStringBuilder
        .append(commonAuthURL)
        .append("?")
        .append(SAMLSSOConstants.SESSION_DATA_KEY)
        .append("=")
        .append(sessionDataKey)
        .append("&")
        .append(FrameworkConstants.RequestParams.TYPE)
        .append("=")
        .append(FrameworkConstants.RequestType.CLAIM_TYPE_SAML_SSO);
    FrameworkUtils.setRequestPathCredentials(req);
    resp.sendRedirect(queryStringBuilder.toString());
  }
  private void handleIdPInitSSO(
      HttpServletRequest req,
      HttpServletResponse resp,
      String relayState,
      String queryString,
      String authMode,
      String sessionId,
      boolean isPost,
      boolean isLogout)
      throws UserStoreException, IdentityException, IOException, ServletException {

    String rpSessionId =
        CharacterEncoder.getSafeText(req.getParameter(MultitenantConstants.SSO_AUTH_SESSION_ID));
    SAMLSSOService samlSSOService = new SAMLSSOService();

    SAMLSSOReqValidationResponseDTO signInRespDTO =
        samlSSOService.validateIdPInitSSORequest(
            relayState,
            queryString,
            getQueryParams(req),
            CarbonUIUtil.getAdminConsoleURL(req),
            sessionId,
            rpSessionId,
            authMode,
            isLogout);

    if (!signInRespDTO.isLogOutReq()) {
      if (signInRespDTO.isValid()) {
        sendToFrameworkForAuthentication(req, resp, signInRespDTO, relayState, false);
      } else {
        if (log.isDebugEnabled()) {
          log.debug("Invalid IdP initiated SAML SSO Request");
        }

        String errorResp = signInRespDTO.getResponse();
        sendNotification(
            errorResp,
            SAMLSSOConstants.Notification.EXCEPTION_STATUS,
            SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
            signInRespDTO.getAssertionConsumerURL(),
            req,
            resp);
      }
    } else {
      if (signInRespDTO.isValid()) {
        sendToFrameworkForLogout(req, resp, signInRespDTO, relayState, sessionId, false, isPost);
      } else {
        if (log.isDebugEnabled()) {
          log.debug("Invalid IdP initiated SAML Single Logout Request");
        }

        if (signInRespDTO.isLogoutFromAuthFramework()) {
          sendToFrameworkForLogout(req, resp, null, null, sessionId, true, isPost);
        } else {
          String errorResp = signInRespDTO.getResponse();
          sendNotification(
              errorResp,
              SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS,
              SAMLSSOConstants.Notification.EXCEPTION_MESSAGE,
              signInRespDTO.getAssertionConsumerURL(),
              req,
              resp);
        }
      }
    }
  }