protected LogoutRequest buildLogoutRequest(String user, String sessionIdx)
      throws SSOAgentException {

    LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject();

    logoutReq.setID(SSOAgentUtils.createID());
    logoutReq.setDestination(ssoAgentConfig.getSAML2().getIdPURL());

    DateTime issueInstant = new DateTime();
    logoutReq.setIssueInstant(issueInstant);
    logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000));

    IssuerBuilder issuerBuilder = new IssuerBuilder();
    Issuer issuer = issuerBuilder.buildObject();
    issuer.setValue(ssoAgentConfig.getSAML2().getSPEntityId());
    logoutReq.setIssuer(issuer);

    NameID nameId = new NameIDBuilder().buildObject();
    nameId.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
    nameId.setValue(user);
    logoutReq.setNameID(nameId);

    SessionIndex sessionIndex = new SessionIndexBuilder().buildObject();
    sessionIndex.setSessionIndex(sessionIdx);
    logoutReq.getSessionIndexes().add(sessionIndex);

    logoutReq.setReason("Single Logout");

    return logoutReq;
  }
  private LogoutRequest buildLogoutRequest(
      String user,
      String sessionIndexStr,
      String idpUrl,
      String nameQualifier,
      String spNameQualifier)
      throws SAMLSSOException {

    LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject();

    logoutReq.setID(SSOUtils.createID());
    logoutReq.setDestination(idpUrl);

    DateTime issueInstant = new DateTime();
    logoutReq.setIssueInstant(issueInstant);
    logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000));

    IssuerBuilder issuerBuilder = new IssuerBuilder();
    Issuer issuer = issuerBuilder.buildObject();

    String spEntityId =
        properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.SP_ENTITY_ID);

    if (spEntityId != null && !spEntityId.isEmpty()) {
      issuer.setValue(spEntityId);
    } else {
      issuer.setValue("carbonServer");
    }

    logoutReq.setIssuer(issuer);

    NameID nameId = new NameIDBuilder().buildObject();
    nameId.setFormat(NameIDType.UNSPECIFIED);
    nameId.setValue(user);
    nameId.setNameQualifier(nameQualifier);
    nameId.setSPNameQualifier(spNameQualifier);
    logoutReq.setNameID(nameId);

    SessionIndex sessionIndex = new SessionIndexBuilder().buildObject();

    if (sessionIndexStr != null) {
      sessionIndex.setSessionIndex(sessionIndexStr);
    } else {
      sessionIndex.setSessionIndex(UUID.randomUUID().toString());
    }

    logoutReq.getSessionIndexes().add(sessionIndex);
    logoutReq.setReason("Single Logout");

    return logoutReq;
  }
  /**
   * Returns logout request message ready to be sent to the IDP.
   *
   * @param context message context
   * @param credential information about assertions used to log current user in
   * @param bindingService service used to deliver the request
   * @return logoutRequest to be sent to IDP
   * @throws SAMLException error creating the message
   * @throws MetadataProviderException error retrieving metadata
   */
  protected LogoutRequest getLogoutRequest(
      SAMLMessageContext context, SAMLCredential credential, Endpoint bindingService)
      throws SAMLException, MetadataProviderException {

    SAMLObjectBuilder<LogoutRequest> builder =
        (SAMLObjectBuilder<LogoutRequest>)
            builderFactory.getBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME);
    LogoutRequest request = builder.buildObject();
    buildCommonAttributes(context.getLocalEntityId(), request, bindingService);

    // Add session indexes
    SAMLObjectBuilder<SessionIndex> sessionIndexBuilder =
        (SAMLObjectBuilder<SessionIndex>)
            builderFactory.getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME);
    for (AuthnStatement statement : credential.getAuthenticationAssertion().getAuthnStatements()) {
      SessionIndex index = sessionIndexBuilder.buildObject();
      index.setSessionIndex(statement.getSessionIndex());
      request.getSessionIndexes().add(index);
    }

    if (request.getSessionIndexes().size() == 0) {
      throw new SAMLException("No session indexes to logout user for were found");
    }

    SAMLObjectBuilder<NameID> nameIDBuilder =
        (SAMLObjectBuilder<NameID>) builderFactory.getBuilder(NameID.DEFAULT_ELEMENT_NAME);
    NameID nameID = nameIDBuilder.buildObject();
    nameID.setFormat(credential.getNameID().getFormat());
    nameID.setNameQualifier(credential.getNameID().getNameQualifier());
    nameID.setSPNameQualifier(credential.getNameID().getSPNameQualifier());
    nameID.setSPProvidedID(credential.getNameID().getSPProvidedID());
    nameID.setValue(credential.getNameID().getValue());
    request.setNameID(nameID);

    return request;
  }
  /**
   * @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);
    }
  }
  public boolean processLogoutRequest(SAMLMessageContext context, SAMLCredential credential)
      throws SAMLException, MetadataProviderException, MessageEncodingException {

    SAMLObject message = context.getInboundSAMLMessage();

    // Verify type
    if (message == null || !(message instanceof LogoutRequest)) {
      log.warn("Received request is not of a LogoutRequest object type");
      throw new SAMLException("Error validating SAML request");
    }

    LogoutRequest logoutRequest = (LogoutRequest) message;

    // Make sure request was authenticated if required, authentication is done as part of the
    // binding processing
    if (!context.isInboundSAMLMessageAuthenticated()
        && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) {
      log.warn(
          "Logout Request object is required to be signed by the entity policy: "
              + context.getInboundSAMLMessageId());
      Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Message signature is required");
      sendLogoutResponse(status, context);
      return false;
    }

    try {
      // Verify destination
      verifyEndpoint(context.getLocalEntityEndpoint(), logoutRequest.getDestination());
    } catch (SAMLException e) {
      log.warn(
          "Destination of the request {} does not match any singleLogout endpoint",
          logoutRequest.getDestination());
      Status status =
          getStatus(StatusCode.REQUEST_DENIED_URI, "Destination URL of the request is invalid");
      sendLogoutResponse(status, context);
      return false;
    }

    // Verify issuer
    if (logoutRequest.getIssuer() != null) {
      try {
        Issuer issuer = logoutRequest.getIssuer();
        verifyIssuer(issuer, context);
      } catch (SAMLException e) {
        log.warn(
            "Response issue time is either too old or with date in the future, id {}",
            context.getInboundSAMLMessageId());
        Status status =
            getStatus(StatusCode.REQUEST_DENIED_URI, "Issuer of the message is unknown");
        sendLogoutResponse(status, context);
        return false;
      }
    }

    // Verify issue time
    DateTime time = logoutRequest.getIssueInstant();
    if (!isDateTimeSkewValid(getResponseSkew(), time)) {
      log.warn(
          "Response issue time is either too old or with date in the future, id {}.",
          context.getInboundSAMLMessageId());
      Status status =
          getStatus(StatusCode.REQUESTER_URI, "Message has been issued too long time ago");
      sendLogoutResponse(status, context);
      return false;
    }

    // Check whether any user is logged in
    if (credential == null) {
      Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "No user is logged in");
      sendLogoutResponse(status, context);
      return false;
    }

    // Find index for which the logout is requested
    boolean indexFound = false;
    if (logoutRequest.getSessionIndexes() != null && logoutRequest.getSessionIndexes().size() > 0) {
      for (AuthnStatement statement :
          credential.getAuthenticationAssertion().getAuthnStatements()) {
        String statementIndex = statement.getSessionIndex();
        if (statementIndex != null) {
          for (SessionIndex index : logoutRequest.getSessionIndexes()) {
            if (statementIndex.equals(index.getSessionIndex())) {
              indexFound = true;
            }
          }
        }
      }
    } else {
      indexFound = true;
    }

    // Fail if sessionIndex is not found in any assertion
    if (!indexFound) {

      // Check logout request still valid and store request
      if (logoutRequest.getNotOnOrAfter() != null) {
        // TODO store request for assertions possibly arriving later
      }

      Status status =
          getStatus(StatusCode.REQUESTER_URI, "The requested SessionIndex was not found");
      sendLogoutResponse(status, context);
      return false;
    }

    try {
      // Fail if NameId doesn't correspond to the currently logged user
      NameID nameID = getNameID(context, logoutRequest);
      if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) {
        Status status =
            getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid");
        sendLogoutResponse(status, context);
        return false;
      }
    } catch (DecryptionException e) {
      Status status = getStatus(StatusCode.RESPONDER_URI, "The NameID can't be decrypted");
      sendLogoutResponse(status, context);
      return false;
    }

    // Message is valid, let's logout
    Status status = getStatus(StatusCode.SUCCESS_URI, null);
    sendLogoutResponse(status, context);

    return true;
  }