Skip to content

Commit

Permalink
AUT-1912 Update to Spring Boot 3, update OpenSAML
Browse files Browse the repository at this point in the history
  • Loading branch information
jaagupkymmel authored and Marten332 committed Oct 25, 2024
1 parent 2fbac77 commit 84368ba
Show file tree
Hide file tree
Showing 47 changed files with 438 additions and 343 deletions.
52 changes: 12 additions & 40 deletions eidas-client-opensaml-extension/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,20 @@
<commons-collections4.version>4.4</commons-collections4.version>
<guava.version>33.1.0-jre</guava.version>
<jose4j.version>0.9.6</jose4j.version>
<!-- TODO AUT-908 Upgrade to OpenSAML 4.x -->
<opensaml.version>3.4.6</opensaml.version>
<!-- TODO 2.x isn't compatible with Java 8. -->
<opensaml-security-ext.version>1.0.8</opensaml-security-ext.version>
<!-- TODO 8.x isn't compatible with Java 8. -->
<shibboleth-java-support.version>7.5.2</shibboleth-java-support.version>
<!-- TODO 6.x isn't compatible with Java 8. -->
<shibboleth-spring-extensions.version>5.4.2</shibboleth-spring-extensions.version>
<!-- TODO 2.x isn't compatible with Java 8. -->
<shiro.version>1.13.0</shiro.version>
<xmlsec.version>3.0.2</xmlsec.version><!-- TODO Does it work with >= 4.x ? -->

<!-- Fix CVE-2020-13936 by replacing velocity with new version of velocity-engine-core.
TODO Remove this property after upgrading opensaml-saml-impl.-->
<velocity.version>2.3</velocity.version>
<opensaml.version>5.1.1</opensaml.version>
<opensaml-security-ext.version>4.1.1</opensaml-security-ext.version>
<shiro.version>2.0.1</shiro.version>
</properties>

<dependencies>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<artifactId>opensaml-core-api</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core-impl</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
Expand All @@ -50,12 +43,6 @@
<artifactId>opensaml-saml-impl</artifactId>
<version>${opensaml.version}</version>
<exclusions>
<!-- Fix CVE-2020-13936 by replacing velocity with new version of velocity-engine-core.
TODO Remove this property after upgrading opensaml-saml-impl.-->
<exclusion>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</exclusion>
<!-- Excluded because we are already using bcprov-jdk18on as a dependency.-->
<exclusion>
<groupId>org.bouncycastle</groupId>
Expand All @@ -64,9 +51,9 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-spring</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
Expand All @@ -90,16 +77,6 @@
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>net.shibboleth.utilities</groupId>
<artifactId>java-support</artifactId>
<version>${shibboleth-java-support.version}</version>
</dependency>
<dependency>
<groupId>net.shibboleth.ext</groupId>
<artifactId>spring-extensions</artifactId>
<version>${shibboleth-spring-extensions.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand All @@ -123,11 +100,6 @@
<version>${guava.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
<version>${xmlsec.version}</version>
</dependency>

<!-- hazelcast and it's custom content protection -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import ee.ria.eidas.client.authnrequest.AuthnRequestBuilder;
import ee.ria.eidas.client.authnrequest.EidasAttribute;
import ee.ria.eidas.client.authnrequest.EidasHTTPPostEncoder;
import ee.ria.eidas.client.authnrequest.NonNotifiedAssuranceLevel;
import ee.ria.eidas.client.authnrequest.SPType;
import ee.ria.eidas.client.config.EidasClientProperties;
import ee.ria.eidas.client.exception.EidasClientException;
Expand All @@ -14,11 +13,11 @@
import ee.ria.eidas.client.session.RequestSessionService;
import ee.ria.eidas.client.session.UnencodedRequestSession;
import ee.ria.eidas.client.util.OpenSAMLUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.shared.component.ComponentInitializationException;
import org.apache.commons.collections.CollectionUtils;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.encoder.MessageEncodingException;
import org.opensaml.saml.common.messaging.context.SAMLEndpointContext;
Expand All @@ -28,20 +27,19 @@
import org.opensaml.xmlsec.SignatureSigningParameters;
import org.opensaml.xmlsec.context.SecurityParametersContext;
import org.springframework.context.ApplicationEventPublisher;
import org.w3c.dom.Element;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static ee.ria.eidas.client.authnrequest.EidasAttribute.*;
import static ee.ria.eidas.client.authnrequest.EidasAttribute.CURRENT_FAMILY_NAME;
import static ee.ria.eidas.client.authnrequest.EidasAttribute.CURRENT_GIVEN_NAME;
import static ee.ria.eidas.client.authnrequest.EidasAttribute.DATE_OF_BIRTH;
import static ee.ria.eidas.client.authnrequest.EidasAttribute.PERSON_IDENTIFIER;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

Expand Down Expand Up @@ -155,7 +153,7 @@ private void redirectUserWithRequest(HttpServletResponse httpServletResponse, Au
encoder.setCountryCode(country.toUpperCase());
encoder.setRelayState(relayState);

encoder.setHttpServletResponse(httpServletResponse);
encoder.setHttpServletResponseSupplier(() -> httpServletResponse);

try {
encoder.initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
import ee.ria.eidas.client.session.RequestSession;
import ee.ria.eidas.client.session.RequestSessionService;
import ee.ria.eidas.client.util.OpenSAMLUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.net.URIComparator;
import net.shibboleth.utilities.java.support.net.URIException;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.resolver.CriteriaSet;
import net.shibboleth.shared.resolver.ResolverException;
import org.apache.commons.lang3.StringUtils;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.messaging.context.MessageContext;
Expand All @@ -34,7 +32,11 @@
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.criterion.EntityRoleCriterion;
import org.opensaml.saml.criterion.ProtocolCriterion;
import org.opensaml.saml.saml2.core.*;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.StatusCode;
import org.opensaml.saml.saml2.core.StatusMessage;
import org.opensaml.saml.saml2.encryption.Decrypter;
import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
Expand All @@ -49,10 +51,10 @@
import se.swedenconnect.opensaml.xmlsec.encryption.support.DecryptionUtils;
import se.swedenconnect.opensaml.xmlsec.encryption.support.Pkcs11Decrypter;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.xml.validation.Schema;
import java.io.ByteArrayInputStream;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
Expand All @@ -73,7 +75,7 @@ public class AuthResponseService {

private final Schema samlSchema;

public AuthenticationResult getAuthenticationResult(HttpServletRequest req) throws MissingServletRequestParameterException {
public AuthenticationResult getAuthenticationResult(HttpServletRequest req) throws MissingServletRequestParameterException, ComponentInitializationException {
try {
Response samlResponse = getSamlResponse(req);

Expand Down Expand Up @@ -168,9 +170,9 @@ private void validateStatusCode(Response samlResponse) {
} else {
log.info("AuthnResponse validation: FAILURE");
if (substatusCode == null)
throw new AuthenticationFailedException(statusMessage.getMessage(), statusCode.getValue());
throw new AuthenticationFailedException(statusMessage.getValue(), statusCode.getValue());
else
throw new AuthenticationFailedException(statusMessage.getMessage(), statusCode.getValue(), substatusCode.getValue());
throw new AuthenticationFailedException(statusMessage.getValue(), statusCode.getValue(), substatusCode.getValue());
}
}

Expand All @@ -184,34 +186,35 @@ private boolean isStatusNoConsentGiven(StatusCode statusCode, StatusCode substat
&& (substatusCode != null && requestDenied.equals(substatusCode.getValue()));
}

private void validateDestinationAndLifetime(Response samlResponse, HttpServletRequest request) {
MessageContext context = new MessageContext<Response>();
private void validateDestinationAndLifetime(Response samlResponse, HttpServletRequest request) throws ComponentInitializationException {
MessageContext context = new MessageContext();
context.setMessage(samlResponse);
SAMLMessageInfoContext messageInfoContext = context.getSubcontext(SAMLMessageInfoContext.class, true);
messageInfoContext.setMessageIssueInstant(samlResponse.getIssueInstant());

SchemaValidateXMLMessage schemaValidationFilter = new SchemaValidateXMLMessage(samlSchema);

MessageLifetimeSecurityHandler lifetimeSecurityHandler = new MessageLifetimeSecurityHandler();
lifetimeSecurityHandler.setClockSkew(eidasClientProperties.getAcceptedClockSkew() * 1000L);
lifetimeSecurityHandler.setMessageLifetime(eidasClientProperties.getResponseMessageLifetime() * 1000L);
lifetimeSecurityHandler.setClockSkew(Duration.ofSeconds(eidasClientProperties.getAcceptedClockSkew()));
lifetimeSecurityHandler.setMessageLifetime(Duration.ofSeconds(eidasClientProperties.getResponseMessageLifetime()));
lifetimeSecurityHandler.setRequiredRule(true);

ReceivedEndpointSecurityHandler receivedEndpointSecurityHandler = new ReceivedEndpointSecurityHandler();
receivedEndpointSecurityHandler.setHttpServletRequest(request);
receivedEndpointSecurityHandler.setHttpServletRequestSupplier(() -> request);
List handlers = new ArrayList<MessageHandler>();

handlers.add(schemaValidationFilter);
handlers.add(lifetimeSecurityHandler);
handlers.add(receivedEndpointSecurityHandler);
receivedEndpointSecurityHandler.setURIComparator(new URIComparator() {
@Override
public boolean compare(@Nullable String messageDestination, @Nullable String receiverEndpoint) throws URIException {
return messageDestination != null && receiverEndpoint != null && messageDestination.equals(eidasClientProperties.getCallbackUrl());
}
receivedEndpointSecurityHandler.setURIComparator((messageDestination, receiverEndpoint) -> {
return messageDestination != null &&
receiverEndpoint != null &&
messageDestination.equals(eidasClientProperties.getCallbackUrl());
});

BasicMessageHandlerChain<ArtifactResponse> handlerChain = new BasicMessageHandlerChain<>();
receivedEndpointSecurityHandler.initialize();
lifetimeSecurityHandler.initialize();
schemaValidationFilter.initialize();
BasicMessageHandlerChain handlerChain = new BasicMessageHandlerChain();
handlerChain.setHandlers(handlers);

try {
Expand All @@ -234,7 +237,7 @@ private RequestSession getAndValidateRequestSession(Response samlResponse) {
} else if (!requestSession.getRequestId().equals(requestID)) {
throw new EidasClientException("Request session ID mismatch!");
} else {
DateTime now = new DateTime(requestSession.getIssueInstant().getZone());
Instant now = Instant.now();
int maxAuthenticationLifetime = eidasClientProperties.getMaximumAuthenticationLifetime();
int acceptedClockSkew = eidasClientProperties.getAcceptedClockSkew();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import ee.ria.eidas.client.util.SAMLSigner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.schema.XSAny;
import org.opensaml.core.xml.schema.impl.XSAnyBuilder;
Expand All @@ -27,6 +26,7 @@
import org.springframework.context.ApplicationEventPublisher;

import javax.xml.namespace.QName;
import java.time.Instant;
import java.util.List;
import java.util.Optional;

Expand All @@ -47,7 +47,7 @@ public class AuthnRequestBuilder {
public AuthnRequest buildAuthnRequest(AssuranceLevel loa, List<EidasAttribute> eidasAttributes, SPType spType, String requesterId, String country) {
try {
AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
authnRequest.setIssueInstant(new DateTime());
authnRequest.setIssueInstant(Instant.now());
authnRequest.setForceAuthn(true);
authnRequest.setIsPassive(false);
authnRequest.setProviderName(eidasClientProperties.getProviderName());
Expand Down Expand Up @@ -94,15 +94,15 @@ private Issuer buildIssuer() {
private RequestedAuthnContext buildRequestedAuthnContext(AssuranceLevel loa, String country) {

AuthnContextClassRef loaAuthnContextClassRef = OpenSAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
loaAuthnContextClassRef.setAuthnContextClassRef(loa.getUri());
loaAuthnContextClassRef.setURI(loa.getUri());
RequestedAuthnContext requestedAuthnContext = OpenSAMLUtils.buildSAMLObject(RequestedAuthnContext.class);
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
requestedAuthnContext.getAuthnContextClassRefs().add(loaAuthnContextClassRef);

if (eidasClientProperties.getNonNotifiedAssuranceLevels() != null) {
Optional<NonNotifiedAssuranceLevel> nonNotifiedLevel = getNonNotifiedLevel(loa, country);
if (nonNotifiedLevel.isPresent()) {
loaAuthnContextClassRef.setAuthnContextClassRef(nonNotifiedLevel.get().getNonNotifiedLevel());
loaAuthnContextClassRef.setURI(nonNotifiedLevel.get().getNonNotifiedLevel());
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.encoder.MessageEncodingException;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.saml2.binding.encoding.impl.HTTPPostEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EidasHTTPPostEncoder extends HTTPPostEncoder {

Expand All @@ -26,7 +23,7 @@ public EidasHTTPPostEncoder() {
}

@Override
protected void populateVelocityContext(VelocityContext velocityContext, MessageContext<SAMLObject> messageContext, String endpointURL) throws MessageEncodingException {
protected void populateVelocityContext(VelocityContext velocityContext, MessageContext messageContext, String endpointURL) throws MessageEncodingException {
velocityContext.put("Country", countryCode);
velocityContext.put("RelayState", relayState);
super.populateVelocityContext(velocityContext, messageContext, endpointURL);
Expand All @@ -39,4 +36,4 @@ public void setCountryCode(String countryCode) {
public void setRelayState(String relayState) {
this.relayState = relayState;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import ee.ria.eidas.client.metadata.SPMetadataGenerator;
import ee.ria.eidas.client.session.LocalRequestSessionServiceImpl;
import ee.ria.eidas.client.session.RequestSessionService;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.shared.component.ComponentInitializationException;
import org.opensaml.saml.common.xml.SAMLSchemaBuilder;
import org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver;
import org.opensaml.saml.security.impl.MetadataCredentialResolver;
Expand All @@ -33,9 +33,10 @@
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;

@Configuration
@ConfigurationPropertiesScan
@EnableScheduling
// TODO Investigate if two bean methos with same name (idpMetadataSignatureTrustEngine) are really necessary or can be renamed to make situation clearer. Currently renaming breaks OpenSAML signature validation certificate loading.
@Configuration(enforceUniqueMethods = false)
public class EidasClientConfiguration {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import ee.ria.eidas.client.authnrequest.AssuranceLevel;
import ee.ria.eidas.client.authnrequest.EidasAttribute;
import ee.ria.eidas.client.authnrequest.NonNotifiedAssuranceLevel;
import jakarta.annotation.PostConstruct;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Data;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Nonnegative;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.PositiveOrZero;
import java.util.Arrays;
import java.util.List;

Expand Down Expand Up @@ -153,6 +153,7 @@ public static class HsmProperties {

@Override
public String toString() {
// The -- at the beginning of the return string is required in order to configure our PKCS11 security provider by using the HsmProperties defined here instead of a .cfg file
if (slot != null) {
return format("--name=eidas\nlibrary=%s\nslot=%s\n", library, slot);
} else {
Expand Down
Loading

0 comments on commit 84368ba

Please sign in to comment.