/**
  * Determines whether given SingleSignOn service can be used together with this profile. Bindings
  * POST, Artifact and Redirect are supported for WebSSO.
  *
  * @param endpoint endpoint
  * @return true if endpoint is supported
  * @throws MetadataProviderException in case system can't verify whether endpoint is supported or
  *     not
  */
 protected boolean isEndpointSupported(SingleSignOnService endpoint)
     throws MetadataProviderException {
   return org.opensaml.common.xml.SAMLConstants.SAML2_POST_BINDING_URI.equals(
           endpoint.getBinding())
       || org.opensaml.common.xml.SAMLConstants.SAML2_ARTIFACT_BINDING_URI.equals(
           endpoint.getBinding())
       || org.opensaml.common.xml.SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(
           endpoint.getBinding());
 }
  /**
   * Encodes the SAML 2.0 based request XML object into its corresponding Base64 notation, based on
   * the type of SAML 2.0 binding.
   *
   * @param requestMessage the {@link RequestAbstractType} XML object to be encoded
   * @param binding the SAML 2.0 binding type
   * @return encoded {@link String} corresponding to the request XML object
   * @throws SSOException if an error occurs while encoding SAML2 request
   */
  protected static String encodeRequestMessage(RequestAbstractType requestMessage, String binding)
      throws SSOException {
    Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(requestMessage);
    Element authDOM;
    try {
      //  Marshall this element, and its children, and root them in a newly created Document
      authDOM = marshaller.marshall(requestMessage);
    } catch (MarshallingException e) {
      throw new SSOException(
          "Error occurred while encoding SAML2 request, failed to marshall the SAML 2.0. "
              + "Request element XMLObject to its corresponding W3C DOM element",
          e);
    }

    StringWriter writer = new StringWriter();
    //  Writes the node out to the writer using the DOM
    XMLHelper.writeNode(authDOM, writer);

    if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(binding)) {
      //  Compress the message, Base 64 encode and URL encode
      Deflater deflater = new Deflater(Deflater.DEFLATED, true);
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      try (DeflaterOutputStream deflaterOutputStream =
          new DeflaterOutputStream(byteArrayOutputStream, deflater)) {
        deflaterOutputStream.write(writer.toString().getBytes(Charset.forName("UTF-8")));
      } catch (IOException e) {
        throw new SSOException("Error occurred while deflate encoding SAML2 request", e);
      }

      String encodedRequestMessage =
          Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES);
      try {
        return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();
      } catch (UnsupportedEncodingException e) {
        throw new SSOException("Error occurred while encoding SAML2 request", e);
      }
    } else if (SAMLConstants.SAML2_POST_BINDING_URI.equals(binding)) {
      return Base64.encodeBytes(
          writer.toString().getBytes(Charset.forName("UTF-8")), Base64.DONT_BREAK_LINES);
    } else {
      logger.log(
          Level.FINE,
          "Unsupported SAML2 HTTP Binding. Defaulting to " + SAMLConstants.SAML2_POST_BINDING_URI);
      return Base64.encodeBytes(
          writer.toString().getBytes(Charset.forName("UTF-8")), Base64.DONT_BREAK_LINES);
    }
  }
  protected String encodeRequestMessage(RequestAbstractType requestMessage, String binding)
      throws SSOAgentException {

    Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(requestMessage);
    Element authDOM = null;
    try {
      authDOM = marshaller.marshall(requestMessage);
      StringWriter rspWrt = new StringWriter();
      XMLHelper.writeNode(authDOM, rspWrt);
      if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(binding)) {
        // Compress the message, Base 64 encode and URL encode
        Deflater deflater = new Deflater(Deflater.DEFLATED, true);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DeflaterOutputStream deflaterOutputStream =
            new DeflaterOutputStream(byteArrayOutputStream, deflater);
        deflaterOutputStream.write(rspWrt.toString().getBytes(Charset.forName("UTF-8")));
        deflaterOutputStream.close();
        String encodedRequestMessage =
            Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES);
        return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();
      } else if (SAMLConstants.SAML2_POST_BINDING_URI.equals(binding)) {
        return Base64.encodeBytes(rspWrt.toString().getBytes(), Base64.DONT_BREAK_LINES);
      } else {
        LOGGER.log(
            Level.FINE,
            "Unsupported SAML2 HTTP Binding. Defaulting to "
                + SAMLConstants.SAML2_POST_BINDING_URI);
        return Base64.encodeBytes(rspWrt.toString().getBytes(), Base64.DONT_BREAK_LINES);
      }
    } catch (MarshallingException e) {
      throw new SSOAgentException("Error occurred while encoding SAML2 request", e);
    } catch (UnsupportedEncodingException e) {
      throw new SSOAgentException("Error occurred while encoding SAML2 request", e);
    } catch (IOException e) {
      throw new SSOAgentException("Error occurred while encoding SAML2 request", e);
    }
  }
Example #4
0
  @Override
  protected void internalInit() {

    CommonHelper.assertTrue(
        CommonHelper.isNotBlank(this.idpMetadata) || CommonHelper.isNotBlank(this.idpMetadataPath),
        "Either idpMetadata or idpMetadataPath must be provided");
    CommonHelper.assertNotBlank("callbackUrl", this.callbackUrl);
    if (!this.callbackUrl.startsWith("http")) {
      throw new TechnicalException("SAML callbackUrl must be absolute");
    }

    if (CommonHelper.isNotBlank(this.keystorePath)
        || CommonHelper.isNotBlank(this.keystorePassword)
        || CommonHelper.isNotBlank(this.privateKeyPassword)) {
      CommonHelper.assertNotBlank("keystorePath", this.keystorePath);
      CommonHelper.assertNotBlank("keystorePassword", this.keystorePassword);
      CommonHelper.assertNotBlank("privateKeyPassword", this.privateKeyPassword);

      // load private key from the keystore and provide it as OpenSAML credentials
      this.credentialProvider =
          new CredentialProvider(this.keystorePath, this.keystorePassword, this.privateKeyPassword);
      this.decrypter = new EncryptionProvider(this.credentialProvider).buildDecrypter();
    }

    // Bootsrap OpenSAML
    try {
      DefaultBootstrap.bootstrap();
      NamedKeyInfoGeneratorManager manager =
          Configuration.getGlobalSecurityConfiguration().getKeyInfoGeneratorManager();
      X509KeyInfoGeneratorFactory generator = new X509KeyInfoGeneratorFactory();
      generator.setEmitEntityCertificate(true);
      generator.setEmitEntityCertificateChain(true);
      manager.registerFactory(Saml2Client.SAML_METADATA_KEY_INFO_GENERATOR, generator);
    } catch (ConfigurationException e) {
      throw new SamlException("Error bootstrapping OpenSAML", e);
    }

    // required parserPool for XML processing
    final StaticBasicParserPool parserPool = newStaticBasicParserPool();
    final AbstractMetadataProvider idpMetadataProvider = idpMetadataProvider(parserPool);

    final XMLObject md;
    try {
      md = idpMetadataProvider.getMetadata();
    } catch (MetadataProviderException e) {
      throw new SamlException("Error initializing idpMetadataProvider", e);
    }

    // If no idpEntityId declared, select first EntityDescriptor entityId as our IDP entityId
    if (this.idpEntityId == null) {
      this.idpEntityId = getIdpEntityId(md);
    }

    // Generate our Service Provider metadata
    Saml2MetadataGenerator metadataGenerator = new Saml2MetadataGenerator();
    if (this.credentialProvider != null) {
      metadataGenerator.setCredentialProvider(this.credentialProvider);
      metadataGenerator.setAuthnRequestSigned(true);
    }
    // If the spEntityId is blank, use the callback url
    if (CommonHelper.isBlank(this.spEntityId)) {
      this.spEntityId = getCallbackUrl();
    }
    metadataGenerator.setEntityId(this.spEntityId);
    // Assertion consumer service url is the callback url
    metadataGenerator.setAssertionConsumerServiceUrl(getCallbackUrl());
    // for now same for logout url
    metadataGenerator.setSingleLogoutServiceUrl(getCallbackUrl());
    AbstractMetadataProvider spMetadataProvider = metadataGenerator.buildMetadataProvider();

    // Initialize metadata provider for our SP and get the XML as a String
    try {
      spMetadataProvider.initialize();
      this.spMetadata = metadataGenerator.printMetadata();
    } catch (MetadataProviderException e) {
      throw new TechnicalException("Error initializing spMetadataProvider", e);
    } catch (MarshallingException e) {
      logger.warn("Unable to print SP metadata", e);
    }

    // Put IDP and SP metadata together
    ChainingMetadataProvider metadataManager = new ChainingMetadataProvider();
    try {
      metadataManager.addMetadataProvider(idpMetadataProvider);
      metadataManager.addMetadataProvider(spMetadataProvider);
    } catch (MetadataProviderException e) {
      throw new TechnicalException("Error adding idp or sp metadatas to manager", e);
    }

    // Build the contextProvider
    this.contextProvider =
        new Saml2ContextProvider(metadataManager, this.idpEntityId, this.spEntityId);

    // Get an AuthnRequest builder
    this.authnRequestBuilder =
        new Saml2AuthnRequestBuilder(forceAuth, comparisonType, destinationBindingType);

    // Build the WebSSO handler for sending and receiving SAML2 messages
    MessageEncoder encoder = null;
    if (SAMLConstants.SAML2_POST_BINDING_URI.equals(destinationBindingType)) {
      // Get a velocity engine for the HTTP-POST binding (building of an HTML document)
      VelocityEngine velocityEngine = VelocityEngineFactory.getEngine();
      encoder = new HTTPPostEncoder(velocityEngine, "/templates/saml2-post-binding.vm");
    } else if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(destinationBindingType)) {
      encoder = new HTTPRedirectDeflateEncoder();
    } else {
      throw new UnsupportedOperationException(
          "Binding type - " + destinationBindingType + " is not supported");
    }

    // Do we need binding specific decoder?
    MessageDecoder decoder = new Pac4jHTTPPostDecoder(parserPool);
    this.handler =
        new Saml2WebSSOProfileHandler(
            this.credentialProvider, encoder, decoder, parserPool, destinationBindingType);

    // Build provider for digital signature validation and encryption
    this.signatureTrustEngineProvider = new SignatureTrustEngineProvider(metadataManager);

    // Build the SAML response validator
    this.responseValidator = new Saml2ResponseValidator();
    if (this.maximumAuthenticationLifetime != null) {
      this.responseValidator.setMaximumAuthenticationLifetime(this.maximumAuthenticationLifetime);
    }
  }