/**
   * Initializes SSO by creating AuthnRequest assertion and sending it to the IDP using the default
   * binding. Default IDP is used to send the request.
   *
   * @param options values specified by caller to customize format of sent request
   * @throws SAMLException error initializing SSO
   * @throws SAMLRuntimeException in case context doesn't contain required entities or contains
   *     invalid data
   * @throws MetadataProviderException error retrieving needed metadata
   * @throws MessageEncodingException error forming SAML message
   */
  public void sendAuthenticationRequest(SAMLMessageContext context, WebSSOProfileOptions options)
      throws SAMLException, MetadataProviderException, MessageEncodingException {

    // Verify we deal with a local SP
    if (!SPSSODescriptor.DEFAULT_ELEMENT_NAME.equals(context.getLocalEntityRole())) {
      throw new SAMLException(
          "WebSSO can only be initialized for local SP, but localEntityRole is: "
              + context.getLocalEntityRole());
    }

    // Load the entities from the context
    SPSSODescriptor spDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata();
    IDPSSODescriptor idpssoDescriptor = (IDPSSODescriptor) context.getPeerEntityRoleMetadata();
    ExtendedMetadata idpExtendedMetadata = context.getPeerExtendedMetadata();

    if (spDescriptor == null || idpssoDescriptor == null || idpExtendedMetadata == null) {
      throw new SAMLException(
          "SPSSODescriptor, IDPSSODescriptor or IDPExtendedMetadata are not present in the SAMLContext");
    }
    log.debug(
        "idpExtendedMetadata.getSigningAlgorithm=" + idpExtendedMetadata.getSigningAlgorithm());
    idpExtendedMetadata.setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");

    SingleSignOnService ssoService =
        getSingleSignOnService(options, idpssoDescriptor, spDescriptor);
    AssertionConsumerService consumerService =
        getAssertionConsumerService(options, idpssoDescriptor, spDescriptor);

    AuthnRequest authRequest = getAuthnRequest(context, options, consumerService, ssoService);
    if (authRequest == null) {
      throw new SAMLException("Erreur dans getAuthnRequest null");
    }
    // authRequest.setForceAuthn(Boolean.TRUE);
    log.debug("getAuthnRequest.providerName=" + authRequest.getProviderName());
    // TODO optionally implement support for conditions, subject

    context.setCommunicationProfileId(getProfileIdentifier());
    context.setOutboundMessage(authRequest);
    context.setOutboundSAMLMessage(authRequest);
    context.setPeerEntityEndpoint(ssoService);
    context.setPeerEntityRoleMetadata(idpssoDescriptor);
    context.setPeerExtendedMetadata(idpExtendedMetadata);

    if (options.getRelayState() != null) {
      context.setRelayState(options.getRelayState());
    }

    boolean sign =
        spDescriptor.isAuthnRequestsSigned() || idpssoDescriptor.getWantAuthnRequestsSigned();
    log.debug("signature?" + sign + " avec http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
    context
        .getLocalExtendedMetadata()
        .setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
    sendMessage(context, sign);

    SAMLMessageStorage messageStorage = context.getMessageStorage();
    if (messageStorage != null) {
      messageStorage.storeMessage(authRequest.getID(), authRequest);
    }
  }
  /**
   * Determines endpoint where should the identity provider return the SAML message. Endpoint also
   * implies the used binding. In case assertionConsumerIndex in the WebSSOProfileOptions is
   * specified the endpoint with the given ID is used. Otherwise assertionConsumerService marked as
   * default is used when present, otherwise first found supported assertionConsumerService is used.
   *
   * <p>In case endpoint determined by the webSSOProfileOptions index is not supported by the
   * profile an exception is raised.
   *
   * @param options user supplied preferences
   * @param idpSSODescriptor idp, can be null when no IDP is known in advance
   * @param spDescriptor sp
   * @return consumer service or null
   * @throws MetadataProviderException in case index supplied in options is invalid or unsupported
   *     or no supported consumer service can be found
   */
  protected AssertionConsumerService getAssertionConsumerService(
      WebSSOProfileOptions options, IDPSSODescriptor idpSSODescriptor, SPSSODescriptor spDescriptor)
      throws MetadataProviderException {

    List<AssertionConsumerService> services = spDescriptor.getAssertionConsumerServices();

    // Use user preference
    if (options.getAssertionConsumerIndex() != null) {
      for (AssertionConsumerService service : services) {
        if (options.getAssertionConsumerIndex().equals(service.getIndex())) {
          if (!isEndpointSupported(service)) {
            throw new MetadataProviderException(
                "Endpoint designated by the value in the WebSSOProfileOptions is not supported by this profile");
          } else {
            log.debug(
                "Using consumer service determined by user preference with binding {}",
                service.getBinding());
            return service;
          }
        }
      }
      throw new MetadataProviderException(
          "AssertionConsumerIndex "
              + options.getAssertionConsumerIndex()
              + " not found for spDescriptor "
              + spDescriptor);
    }

    // Use default
    if (spDescriptor.getDefaultAssertionConsumerService() != null
        && isEndpointSupported(spDescriptor.getDefaultAssertionConsumerService())) {
      AssertionConsumerService service = spDescriptor.getDefaultAssertionConsumerService();
      log.debug("Using default consumer service with binding {}", service.getBinding());
      log.debug("Default ACS url=" + service.getLocation());
      return service;
    }

    // Iterate and find first match
    if (services.size() > 0) {
      for (AssertionConsumerService service : services) {
        if (isEndpointSupported(service)) {
          log.debug("Using first available consumer service with binding {}", service.getBinding());
          return service;
        }
      }
    }

    throw new MetadataProviderException(
        "Service provider has no assertion consumer service available for the selected profile "
            + spDescriptor);
  }
Example #3
0
  private void init(InputStream inputStream) throws SAMLException {
    BasicParserPool parsers = new BasicParserPool();
    parsers.setNamespaceAware(true);

    EntityDescriptor edesc;

    try {
      Document doc = parsers.parse(inputStream);
      Element root = doc.getDocumentElement();

      UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();

      edesc = (EntityDescriptor) unmarshallerFactory.getUnmarshaller(root).unmarshall(root);
    } catch (org.opensaml.xml.parse.XMLParserException e) {
      throw new SAMLException(e);
    } catch (org.opensaml.xml.io.UnmarshallingException e) {
      throw new SAMLException(e);
    }

    // fetch sp information
    SPSSODescriptor spDesc = edesc.getSPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol");

    if (spDesc == null) throw new SAMLException("No SP SSO descriptor found");

    // get first redirect or post binding
    String acsUrl = null;
    for (AssertionConsumerService svc : spDesc.getAssertionConsumerServices()) {
      if (svc.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)
          || svc.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) {
        acsUrl = svc.getLocation();
        break;
      }
    }

    if (acsUrl == null) throw new SAMLException("No acceptable Assertion Consumer Service found");

    this.setEntityId(edesc.getEntityID());
    this.setAcs(acsUrl);
  }
  public void processLogoutResponse(SAMLMessageContext context)
      throws SAMLException, org.opensaml.xml.security.SecurityException, ValidationException {

    SAMLObject message = context.getInboundSAMLMessage();

    // Verify type
    if (!(message instanceof LogoutResponse)) {
      log.debug("Received response is not of a Response object type");
      throw new SAMLException("Error validating SAML response");
    }
    LogoutResponse response = (LogoutResponse) message;

    // Make sure request was authenticated if required, authentication is done as part of the
    // binding processing
    if (!context.isInboundSAMLMessageAuthenticated()
        && context.getLocalExtendedMetadata().isRequireLogoutResponseSigned()) {
      log.debug(
          "Logout Response object is required to be signed by the entity policy: "
              + context.getInboundSAMLMessageId());
      throw new SAMLException("Logout Response object is required to be signed");
    }

    // Verify issue time
    DateTime time = response.getIssueInstant();
    if (!isDateTimeSkewValid(getResponseSkew(), time)) {
      log.debug("Response issue time is either too old or with date in the future");
      throw new SAMLException("Error validating SAML response");
    }

    // Verify response to field if present, set request if correct
    // The inResponseTo field is optional, SAML 2.0 Core, 1542
    SAMLMessageStorage messageStorage = context.getMessageStorage();
    if (messageStorage != null && response.getInResponseTo() != null) {
      XMLObject xmlObject = messageStorage.retrieveMessage(response.getInResponseTo());
      if (xmlObject == null) {
        log.debug(
            "InResponseToField doesn't correspond to sent message", response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      } else if (xmlObject instanceof LogoutRequest) {
        // Expected
      } else {
        log.debug(
            "Sent request was of different type then received response",
            response.getInResponseTo());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify destination
    if (response.getDestination() != null) {
      SPSSODescriptor localDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata();

      // Check if destination is correct on this SP
      List<SingleLogoutService> services = localDescriptor.getSingleLogoutServices();
      boolean found = false;
      for (SingleLogoutService service : services) {
        if (response.getDestination().equals(service.getLocation())
            && context.getInboundSAMLBinding().equals(service.getBinding())) {
          found = true;
          break;
        }
      }
      if (!found) {
        log.debug(
            "Destination of the response was not the expected value", response.getDestination());
        throw new SAMLException("Error validating SAML response");
      }
    }

    // Verify issuer
    if (response.getIssuer() != null) {
      Issuer issuer = response.getIssuer();
      verifyIssuer(issuer, context);
    }

    // Verify status
    String statusCode = response.getStatus().getStatusCode().getValue();
    if (StatusCode.SUCCESS_URI.equals(statusCode)) {
      log.trace("Single Logout was successful");
    } else if (StatusCode.PARTIAL_LOGOUT_URI.equals(statusCode)) {
      log.trace("Single Logout was partially successful");
    } else {
      String[] logMessage = new String[2];
      logMessage[0] = response.getStatus().getStatusCode().getValue();
      StatusMessage message1 = response.getStatus().getStatusMessage();
      if (message1 != null) {
        logMessage[1] = message1.getMessage();
      }
      log.warn("Received LogoutResponse has invalid status code", logMessage);
    }
  }