// find eligible local IDPs for the tenant
  private List<String> getEligibleLocalIdpList(AuthnRequestState t) {
    Validate.notNull(t);
    IdmAccessor accessor = t.getIdmAccessor();

    List<String> localIDPs =
        new ArrayList<String>(
            Arrays.asList(accessor.getIdpEntityId(), accessor.getDefaultIdpEntityId()));
    List<String> eligibleLocalIDPs = new ArrayList<>();

    IDPList idpList =
        t.getAuthnRequest().getScoping() == null
            ? null
            : t.getAuthnRequest().getScoping().getIDPList();

    if (idpList != null && idpList.getIDPEntrys() != null) {
      for (IDPEntry entry : idpList.getIDPEntrys()) {
        if (entry != null && localIDPs.contains(entry.getProviderID())) {
          eligibleLocalIDPs.add(entry.getProviderID());
        }
      }
      if (eligibleLocalIDPs.isEmpty()) {
        log.debug("samlp:Scoping:IDPList does not contain VMWare local Identity Store.");
      }
    } else {
      eligibleLocalIDPs = localIDPs;
    }

    return eligibleLocalIDPs;
  }
  /**
   * Builds an IdP List out of the idpEntityNames
   *
   * @param idpEntityNames The IdPs Entity IDs to include in the IdP List, no list is created when
   *     null
   * @param serviceURI The binding service for an IdP for a specific binding. Should be null if
   *     there is more than one IdP in the list or if the destination IdP is not known in advance.
   * @return an IdP List or null when idpEntityNames is null
   */
  protected IDPList buildIDPList(Set<String> idpEntityNames, SingleSignOnService serviceURI) {

    if (idpEntityNames == null) {
      return null;
    }

    SAMLObjectBuilder<IDPEntry> idpEntryBuilder =
        (SAMLObjectBuilder<IDPEntry>) builderFactory.getBuilder(IDPEntry.DEFAULT_ELEMENT_NAME);
    SAMLObjectBuilder<IDPList> idpListBuilder =
        (SAMLObjectBuilder<IDPList>) builderFactory.getBuilder(IDPList.DEFAULT_ELEMENT_NAME);
    IDPList idpList = idpListBuilder.buildObject();

    for (String entityID : idpEntityNames) {
      IDPEntry idpEntry = idpEntryBuilder.buildObject();
      idpEntry.setProviderID(entityID);
      idpList.getIDPEntrys().add(idpEntry);

      // The service URI would be null if the SP does not know in advance
      // to which IdP the request is sent to.
      if (serviceURI != null) {
        idpEntry.setLoc(serviceURI.getLocation());
      }
    }

    return idpList;
  }
  // check if the provided IDPList contains a trusted external IDP
  // return the list of valid ones
  private List<String> findValidExternalIdpListWithinScoping(
      List<IDPEntry> requestIdpList, IdmAccessor accessor) {
    Validate.notNull(requestIdpList);
    Validate.notNull(accessor);
    List<String> retVal = new ArrayList<>();
    Collection<IDPConfig> extIdps = accessor.getExternalIdps();

    if (extIdps == null || extIdps.isEmpty()) {
      log.debug("No external IDP registered! ");
    } else {
      for (IDPEntry entry : requestIdpList) {
        if (entry != null) {
          IDPConfig foundConfig =
              accessor.getExternalIdpConfigForTenant(accessor.getTenant(), entry.getProviderID());
          if (foundConfig != null) {
            retVal.add(entry.getProviderID());
          }
        }
      }
    }
    log.debug("check if IDPList contain a trusted external IDP, result: {}", !retVal.isEmpty());

    return retVal;
  }
  /**
   * Return a SOAP Envelope Body that contains the Response the IdP sent, if there is one.
   *
   * <p>Returns null if the IdP returned no response at all. Nothing.
   *
   * @return
   */
  private Body getResponseBody(
      ExchangeContent spContent, IDPEntry idpEntry, PaosClient paosClient, ClientOptions options) {

    String spAssertionConsumerURL = "";
    ExchangeContent idpContent = null;
    Envelope idpEnvelope = null;
    URL idpURL = null;

    // Extract idplist from authnrequest and check if the SP supports
    // the one that was chosen. If not, complain.
    idpURL = determineIdP(spContent.getResponseParts().getHeader(), idpEntry);

    spAssertionConsumerURL =
        ExtractField.extractAssertionConsumerURL(spContent.getResponseParts().getHeader());

    // If no matching idp was found from the list the SP sent...
    if (idpURL == null) {
      logger.info("The SP did not indicate support for the chosen IdP.");
      idpURL = getURL(idpEntry.getLoc()); // Get an assertion from the IdP
      // and let the SP trust an
      // unknown IdP.
    }

    // Create the envelope with the AuthnRequest that will be sent to the
    // IdP
    idpEnvelope = EnvelopeCreator.createIdpEnvelope(spContent.getResponseParts());

    // Get the Assertion from the IdP (send AuthnRequest to IdP)
    idpContent = getAssertion(paosClient, idpEnvelope, idpURL, options);

    // If the IdP sent back anything at all as a response:
    if (idpContent != null) {
      // Check assertionConsumerURL. If it does not match, send a SOAP
      // fault to the SP/endpoint
      if (consumerUrlsMatch(idpContent, spAssertionConsumerURL)) {
        return idpContent.getResponseParts().getBody();
      } else {
        logger.debug("AssertionConsumerURLs from AuthnRequest and Response did not match.");
        logger.debug("Returning a SOAP fault message to the endpoint.");
        return EnvelopeCreator.createSoapFaultBody("AssertionConsumerURLs did not match.");
      }
    } // else the paosclient has complained about this.
    return null;
  }
  /**
   * Extracts the forced IDP list from the session.
   *
   * @param session The authentication session.
   * @return The forced IDPs.
   * @throws OAException If organization storage exist check can't be performed.
   */
  @SuppressWarnings("unchecked")
  private List<String> getForcedIDPs(ISession session) throws OAException {
    List<String> retval = new Vector<String>();

    IUser oUser = session.getUser();
    if (oUser instanceof SAMLRemoteUser) {
      SAMLRemoteUser remoteUser = (SAMLRemoteUser) oUser;
      String sRemoteIdP = remoteUser.getOrganization();
      if (sRemoteIdP != null && _organizationStorage.exists(sRemoteIdP)) {
        StringBuffer sbDebug = new StringBuffer();
        sbDebug.append("There is a Remote SAML User available in session with ID '");
        sbDebug.append(session.getId());
        sbDebug.append("' that is known at remote IdP '");
        sbDebug.append(sRemoteIdP);
        sbDebug.append("' so this IdP will be forced");
        _logger.debug(sbDebug.toString());
        retval.add(sRemoteIdP);
        return retval;
      }
    }

    ISessionAttributes atts = session.getAttributes();
    String sGetComplete =
        (String) atts.get(ProxyAttributes.class, ProxyAttributes.IDPLIST_GETCOMPLETE);

    if (sGetComplete != null) {
      _logger.debug(
          "Using proxy attribute: " + ProxyAttributes.IDPLIST_GETCOMPLETE + ": " + sGetComplete);
      // getcomplete
      IDPList idpList = null;
      try {
        if (_mRemoteIDPLists.containsKey(sGetComplete)) {
          idpList = _mRemoteIDPLists.get(sGetComplete).getList();
        } else {
          RemoteIDPListEntry entry = new RemoteIDPListEntry(sGetComplete, 1000);
          idpList = entry.getList();

          // DD Add the RemoteIDPListEntry to a map for caching purposes; The getEntry() retrieves
          // the list from the url.
          _mRemoteIDPLists.put(sGetComplete, entry);
        }

        if (idpList != null) {
          for (IDPEntry entry : idpList.getIDPEntrys()) {
            retval.add(entry.getProviderID());
          }
        }
      } catch (ResourceException e) {
        _logger.warn("Failed retrieval of IDPList from GetComplete URL: " + sGetComplete, e);
      }
    }

    List<SAML2IDPEntry> idpList =
        (List<SAML2IDPEntry>) atts.get(ProxyAttributes.class, ProxyAttributes.IDPLIST);
    if (idpList != null) {
      if (_logger.isDebugEnabled()) {
        StringBuffer sbMessage = new StringBuffer("Using proxy attribute ");
        sbMessage.append(ProxyAttributes.IDPLIST);
        sbMessage.append(": ").append(idpList);
        _logger.debug(sbMessage);
      }

      for (SAML2IDPEntry entry : idpList) {
        // DD We currently ignore the proxied SAML2IDPEntry.getName() (friendlyname) and
        // SAML2IDPEntry.getLoc()
        String sID = entry.getProviderID();
        if (sID != null) {
          if (!retval.contains(sID)) retval.add(sID);
        }
      }
    }

    Collection cForcedOrganizations =
        (Collection)
            atts.get(
                com.alfaariss.oa.util.session.ProxyAttributes.class,
                com.alfaariss.oa.util.session.ProxyAttributes.FORCED_ORGANIZATIONS);
    if (cForcedOrganizations != null) {
      if (_logger.isDebugEnabled()) {
        StringBuffer sbMessage = new StringBuffer("Using proxy attribute ");
        sbMessage.append(com.alfaariss.oa.util.session.ProxyAttributes.FORCED_ORGANIZATIONS);
        sbMessage.append(": ").append(cForcedOrganizations);
        _logger.debug(sbMessage);
      }
      for (Object oForceOrganization : cForcedOrganizations) {
        String sForceOrganization = (String) oForceOrganization;
        if (!retval.contains(sForceOrganization)) retval.add(sForceOrganization);
      }
    }

    return retval;
  }
  /**
   * Read the list of supported IDPs that the SP sent and determine if the chosen IdP is supported.
   * Request = opensaml ECP request header.
   *
   * @param header
   * @return
   */
  public URL determineIdP(Header header, IDPEntry idpEntry) {

    IDPList idpList = null;
    List<XMLObject> list = header.getUnknownXMLObjects();

    for (XMLObject xmlObject : list) {
      if (xmlObject.getElementQName().equals(Request.DEFAULT_ELEMENT_NAME)) {
        idpList = ((Request) xmlObject).getIDPList();
      }
    }

    // If the list from the SP contains the same entry that
    // was chosen by the client...
    if (idpList != null) {
      for (IDPEntry spIdpEntry : idpList.getIDPEntrys()) {
        if (spIdpEntry.getName() != null
            && spIdpEntry.getLoc() != null
            && idpEntry.getProviderID() != null)
          if (spIdpEntry.getName().equals(idpEntry.getName()))
            if (spIdpEntry.getLoc().equals(idpEntry.getLoc()))
              if (spIdpEntry.getProviderID().equals(idpEntry.getProviderID()))
                return getURL(spIdpEntry.getLoc());
      }
    }
    return null;
  }