diff --git a/build.gradle b/build.gradle index 1e94181831..6657f177f1 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,8 @@ buildscript { common_utils_version = System.getProperty("common_utils.version", '3.0.0.0-SNAPSHOT') kafka_version = '3.5.0' apache_cxf_version = '4.0.2' + open_saml_version = '4.3.0' + one_login_java_saml = '2.9.0' if (buildVersionQualifier) { opensearch_build += "-${buildVersionQualifier}" @@ -42,6 +44,8 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" } maven { url "https://d1nvenhzbhpy0q.cloudfront.net/snapshots/lucene/" } + maven { url "https://build.shibboleth.net/nexus/content/groups/public" } + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases" } } dependencies { @@ -373,6 +377,7 @@ repositories { maven { url "https://plugins.gradle.org/m2/" } maven { url "https://aws.oss.sonatype.org/content/repositories/snapshots" } maven { url "https://d1nvenhzbhpy0q.cloudfront.net/snapshots/lucene/" } + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases" } } tasks.withType(Checkstyle) { @@ -487,8 +492,6 @@ dependencies { implementation 'com.github.wnameless:json-flattener:0.5.0' implementation 'com.flipkart.zjsonpatch:zjsonpatch:0.4.4' implementation "org.apache.kafka:kafka-clients:${kafka_version}" - implementation 'com.onelogin:java-saml:2.5.0' - implementation 'com.onelogin:java-saml-core:2.5.0' runtimeOnly 'net.minidev:accessors-smart:2.4.7' @@ -510,23 +513,26 @@ dependencies { testImplementation 'org.apache.camel:camel-xmlsecurity:3.14.2' - implementation 'net.shibboleth.utilities:java-support:7.5.1' - implementation 'org.opensaml:opensaml-core:3.4.5' - implementation 'org.opensaml:opensaml-security-impl:3.4.5' - implementation 'org.opensaml:opensaml-security-api:3.4.5' - implementation 'org.opensaml:opensaml-xmlsec-api:3.4.5' - implementation 'org.opensaml:opensaml-xmlsec-impl:3.4.5' - implementation 'org.opensaml:opensaml-saml-api:3.4.5' - implementation ('org.opensaml:opensaml-saml-impl:3.4.5') { + //OpenSAML + implementation 'net.shibboleth.utilities:java-support:8.4.0' + implementation "com.onelogin:java-saml:${one_login_java_saml}" + implementation "com.onelogin:java-saml-core:${one_login_java_saml}" + implementation "org.opensaml:opensaml-core:${open_saml_version}" + implementation "org.opensaml:opensaml-security-impl:${open_saml_version}" + implementation "org.opensaml:opensaml-security-api:${open_saml_version}" + implementation "org.opensaml:opensaml-xmlsec-api:${open_saml_version}" + implementation "org.opensaml:opensaml-xmlsec-impl:${open_saml_version}" + implementation "org.opensaml:opensaml-saml-api:${open_saml_version}" + implementation ("org.opensaml:opensaml-saml-impl:${open_saml_version}") { exclude(group: 'org.apache.velocity', module: 'velocity') } + implementation "org.opensaml:opensaml-messaging-api:${open_saml_version}" + runtimeOnly "org.opensaml:opensaml-profile-api:${open_saml_version}" + runtimeOnly "org.opensaml:opensaml-soap-api:${open_saml_version}" + runtimeOnly "org.opensaml:opensaml-soap-impl:${open_saml_version}" + implementation "org.opensaml:opensaml-storage-api:${open_saml_version}" + implementation "com.nulab-inc:zxcvbn:1.7.0" - testImplementation 'org.opensaml:opensaml-messaging-impl:3.4.5' - implementation 'org.opensaml:opensaml-messaging-api:3.4.5' - runtimeOnly 'org.opensaml:opensaml-profile-api:3.4.5' - runtimeOnly 'org.opensaml:opensaml-soap-api:3.4.5' - runtimeOnly 'org.opensaml:opensaml-soap-impl:3.4.5' - implementation 'org.opensaml:opensaml-storage-api:3.4.5' implementation 'commons-collections:commons-collections:3.2.2' implementation 'com.jayway.jsonpath:json-path:2.4.0' implementation 'net.minidev:json-smart:2.4.10' @@ -554,6 +560,7 @@ dependencies { runtimeOnly 'org.scala-lang.modules:scala-java8-compat_3:1.0.2' + testImplementation "org.opensaml:opensaml-messaging-impl:${open_saml_version}" implementation 'org.apache.commons:commons-lang3:3.12.0' testImplementation "org.opensearch:common-utils:${common_utils_version}" testImplementation "org.opensearch.plugin:reindex-client:${opensearch_version}" diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java index 1c49d10b2e..7f36635674 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java @@ -168,9 +168,7 @@ private AuthTokenProcessorAction.Response handleImpl( try { - SamlResponse samlResponse = new SamlResponse(saml2Settings, null); - samlResponse.setDestinationUrl(acsEndpoint); - samlResponse.loadXmlFromBase64(samlResponseBase64); + final SamlResponse samlResponse = new SamlResponse(saml2Settings, acsEndpoint, samlResponseBase64); if (!samlResponse.isValid(samlRequestId)) { log.warn("Error while validating SAML response in /_opendistro/_security/api/authtoken"); diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java b/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java index 881c9c3553..1b97242762 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java @@ -14,6 +14,7 @@ import java.security.AccessController; import java.security.PrivateKey; import java.security.PrivilegedAction; +import java.time.Instant; import java.util.AbstractMap; import java.util.Collection; import java.util.HashMap; @@ -28,7 +29,6 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.joda.time.DateTime; import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.saml.metadata.resolver.MetadataResolver; import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver; @@ -54,7 +54,7 @@ public class Saml2SettingsProvider { private final String idpEntityId; private final PrivateKey spSignaturePrivateKey; private Saml2Settings cachedSaml2Settings; - private DateTime metadataUpdateTime; + private Instant metadataUpdateTime; Saml2SettingsProvider(Settings opensearchSettings, MetadataResolver metadataResolver, PrivateKey spSignaturePrivateKey) { this.opensearchSettings = opensearchSettings; @@ -107,7 +107,7 @@ Saml2Settings get() throws SamlConfigException { } Saml2Settings getCached() throws SamlConfigException { - DateTime tempLastUpdate = null; + Instant tempLastUpdate = null; if (this.metadataResolver instanceof RefreshableMetadataResolver && this.isUpdateRequired()) { this.cachedSaml2Settings = null; diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/SamlHTTPMetadataResolver.java b/src/main/java/com/amazon/dlic/auth/http/saml/SamlHTTPMetadataResolver.java index 015e8df12d..2a380539e6 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/SamlHTTPMetadataResolver.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/SamlHTTPMetadataResolver.java @@ -15,6 +15,7 @@ import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.time.Duration; import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.apache.http.client.HttpClient; @@ -31,8 +32,8 @@ public class SamlHTTPMetadataResolver extends HTTPMetadataResolver { SamlHTTPMetadataResolver(String idpMetadataUrl, Settings opensearchSettings, Path configPath) throws Exception { super(createHttpClient(opensearchSettings, configPath), idpMetadataUrl); - setMinRefreshDelay(opensearchSettings.getAsLong("idp.min_refresh_delay", 60L * 1000L)); - setMaxRefreshDelay(opensearchSettings.getAsLong("idp.max_refresh_delay", 14400000L)); + setMinRefreshDelay(Duration.ofMillis(opensearchSettings.getAsLong("idp.min_refresh_delay", 60L * 1000L))); + setMaxRefreshDelay(Duration.ofMillis(opensearchSettings.getAsLong("idp.max_refresh_delay", 14400000L))); setRefreshDelayFactor(opensearchSettings.getAsFloat("idp.refresh_delay_factor", 0.75f)); } diff --git a/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java b/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java index ef54e3e833..c984b4f670 100644 --- a/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java +++ b/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java @@ -23,6 +23,7 @@ import java.net.URISyntaxException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -32,6 +33,8 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -60,6 +63,7 @@ import javax.xml.transform.stream.StreamResult; import net.shibboleth.utilities.java.support.codec.Base64Support; +import net.shibboleth.utilities.java.support.codec.EncodingException; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ClassicHttpRequest; @@ -82,7 +86,6 @@ import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIBuilder; -import org.joda.time.DateTime; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.XMLObjectBuilderFactory; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; @@ -92,7 +95,6 @@ import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.messaging.handler.MessageHandlerException; -import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.SAMLVersion; import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext; import org.opensaml.saml.common.messaging.context.SAMLProtocolContext; @@ -331,11 +333,11 @@ public String handleSsoGetRequestBase(HttpRequest request) { HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder(); decoder.setParserPool(XMLObjectProviderRegistrySupport.getParserPool()); - decoder.setHttpServletRequest(httpServletRequest); + decoder.setHttpServletRequestSupplier(() -> httpServletRequest); decoder.initialize(); decoder.decode(); - MessageContext messageContext = decoder.getMessageContext(); + MessageContext messageContext = decoder.getMessageContext(); if (!(messageContext.getMessage() instanceof AuthnRequest)) { throw new RuntimeException("Expected AuthnRequest; received: " + messageContext.getMessage()); @@ -357,7 +359,6 @@ public void handleSloGetRequestURI(String samlRequestURI) { handleSloGetRequestBase(new BasicHttpRequest("GET", samlRequestURI)); } - @SuppressWarnings("unchecked") public void handleSloGetRequestBase(HttpRequest request) { try { @@ -365,11 +366,11 @@ public void handleSloGetRequestBase(HttpRequest request) { HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder(); decoder.setParserPool(XMLObjectProviderRegistrySupport.getParserPool()); - decoder.setHttpServletRequest(httpServletRequest); + decoder.setHttpServletRequestSupplier(() -> httpServletRequest); decoder.initialize(); decoder.decode(); - MessageContext messageContext = decoder.getMessageContext(); + MessageContext messageContext = decoder.getMessageContext(); if (!(messageContext.getMessage() instanceof LogoutRequest)) { throw new RuntimeException("Expected LogoutRequest; received: " + messageContext.getMessage()); @@ -391,7 +392,7 @@ public void handleSloGetRequestBase(HttpRequest request) { validationParams.setSignatureTrustEngine(buildSignatureTrustEngine(this.spSignatureCertificate)); securityParametersContext.setSignatureValidationParameters(validationParams); - signatureSecurityHandler.setHttpServletRequest(httpServletRequest); + signatureSecurityHandler.setHttpServletRequestSupplier(() -> httpServletRequest); signatureSecurityHandler.initialize(); signatureSecurityHandler.invoke(messageContext); @@ -415,18 +416,18 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { response.setVersion(SAMLVersion.VERSION_20); response.setStatus(createStatus(StatusCode.SUCCESS)); - response.setIssueInstant(new DateTime()); + response.setIssueInstant(Instant.now()); Assertion assertion = createSamlElement(Assertion.class); assertion.setID(nextId()); - assertion.setIssueInstant(new DateTime()); + assertion.setIssueInstant(Instant.now()); assertion.setIssuer(createIssuer()); AuthnStatement authnStatement = createSamlElement(AuthnStatement.class); assertion.getAuthnStatements().add(authnStatement); - authnStatement.setAuthnInstant(new DateTime()); + authnStatement.setAuthnInstant(Instant.now()); authnStatement.setSessionIndex(nextId()); authnStatement.setAuthnContext(createAuthnCotext()); @@ -440,7 +441,7 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { .add( createSubjectConfirmation( "urn:oasis:names:tc:SAML:2.0:cm:bearer", - new DateTime().plusMinutes(1), + Instant.now().plus(1, ChronoUnit.MINUTES), authnRequest.getID(), authnRequest.getAssertionConsumerServiceURL() ) @@ -450,7 +451,7 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { .add( createSubjectConfirmation( "urn:oasis:names:tc:SAML:2.0:cm:bearer", - new DateTime().plusMinutes(1), + Instant.now().plus(1, ChronoUnit.MINUTES), null, defaultAssertionConsumerService ) @@ -460,8 +461,8 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { Conditions conditions = createSamlElement(Conditions.class); assertion.setConditions(conditions); - conditions.setNotBefore(new DateTime()); - conditions.setNotOnOrAfter(new DateTime().plusMinutes(1)); + conditions.setNotBefore(Instant.now()); + conditions.setNotOnOrAfter(Instant.now().plus(1, ChronoUnit.MINUTES)); if (authenticateUserRoles != null) { AttributeStatement attributeStatement = createSamlElement(AttributeStatement.class); @@ -501,9 +502,9 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { String marshalledXml = marshallSamlXml(response); - return Base64Support.encode(marshalledXml.getBytes("UTF-8"), Base64Support.UNCHUNKED); + return Base64Support.encode(marshalledXml.getBytes(StandardCharsets.UTF_8), Base64Support.UNCHUNKED); - } catch (MarshallingException | SignatureException | UnsupportedEncodingException | EncryptionException e) { + } catch (MarshallingException | SignatureException | EncryptionException | EncodingException e) { throw new RuntimeException(e); } } @@ -547,7 +548,7 @@ private NameIDFormat createNameIDFormat(String format) { NameIDFormat nameIdFormat = createSamlElement(NameIDFormat.class); - nameIdFormat.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + nameIdFormat.setURI("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); return nameIdFormat; } @@ -567,7 +568,7 @@ private NameID createNameID(String format, String value) { return nameID; } - private SubjectConfirmation createSubjectConfirmation(String method, DateTime notOnOrAfter, String inResponseTo, String recipient) { + private SubjectConfirmation createSubjectConfirmation(String method, Instant notOnOrAfter, String inResponseTo, String recipient) { SubjectConfirmation result = createSamlElement(SubjectConfirmation.class); result.setMethod(method); @@ -591,7 +592,7 @@ private Issuer createIssuer() { private AuthnContext createAuthnCotext() { AuthnContext authnContext = createSamlElement(AuthnContext.class); AuthnContextClassRef authnContextClassRef = createSamlElement(AuthnContextClassRef.class); - authnContextClassRef.setAuthnContextClassRef(AuthnContext.UNSPECIFIED_AUTHN_CTX); + authnContextClassRef.setURI(AuthnContext.UNSPECIFIED_AUTHN_CTX); authnContext.setAuthnContextClassRef(authnContextClassRef); return authnContext; }