Skip to content

Commit

Permalink
NIFI-14035 Upgraded Spring Security from 6.3.4 to 6.4.0
Browse files Browse the repository at this point in the history
- Upgraded OpenSAML from 4.3.2 to 5.1.3
- Upgraded Nimbus JOSE JWT from 9.42 to 9.47
- Upgraded Santuario from 2.3.4 to 4.0.3
- Updated OpenSAML 4 references to OpenSAML 5
- Added OIDC RestClient Bean and updated references based on deprecation warnings
  • Loading branch information
exceptionfactory committed Nov 19, 2024
1 parent e33cbe6 commit 44a18b4
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,32 +207,28 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-saml2-service-provider</artifactId>
<exclusions>
<!-- Exclude Velocity from OpenSAML -->
<!-- Exclude OpenSAML 4 from Spring Security 6.4 -->
<exclusion>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</exclusion>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<artifactId>opensaml-core-api</artifactId>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-xmlsec-api</artifactId>
<artifactId>opensaml-core-impl</artifactId>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<artifactId>opensaml-saml-impl</artifactId>
</dependency>
<!-- Commons Codec required for OpenSAML -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
Expand Down Expand Up @@ -75,7 +75,7 @@ public ClientRegistrationConfiguration(
public ClientRegistrationRepository clientRegistrationRepository() {
final ClientRegistrationRepository clientRegistrationRepository;
if (properties.isOidcEnabled()) {
final ClientRegistrationProvider clientRegistrationProvider = new StandardClientRegistrationProvider(properties, oidcRestOperations());
final ClientRegistrationProvider clientRegistrationProvider = new StandardClientRegistrationProvider(properties, oidcRestClient());
final ClientRegistration clientRegistration = clientRegistrationProvider.getClientRegistration();
clientRegistrationRepository = new InMemoryClientRegistrationRepository(clientRegistration);
} else {
Expand All @@ -84,14 +84,23 @@ public ClientRegistrationRepository clientRegistrationRepository() {
return clientRegistrationRepository;
}

/**
* OpenID Connect REST Client for communication with Authorization Servers
*
* @return REST Client
*/
@Bean
public RestClient oidcRestClient() {
return RestClient.create(oidcRestOperations());
}

/**
* OpenID Connect REST Operations for communication with Authorization Servers
*
* @return REST Operations
*/
@Bean
public RestOperations oidcRestOperations() {
public RestTemplate oidcRestOperations() {
final RestTemplate restTemplate = new RestTemplate(oidcClientHttpRequestFactory());
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
restTemplate.setMessageConverters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.RestClientRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
Expand All @@ -71,6 +71,7 @@
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestOperations;

import java.time.Duration;
Expand Down Expand Up @@ -110,6 +111,8 @@ public class OidcSecurityConfiguration {

private final RestOperations oidcRestOperations;

private final RestClient oidcRestClient;

private final LogoutRequestManager logoutRequestManager;

@Autowired
Expand All @@ -124,6 +127,8 @@ public OidcSecurityConfiguration(
final JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory,
@Qualifier("oidcRestOperations")
final RestOperations oidcRestOperations,
@Qualifier("oidcRestClient")
final RestClient oidcRestClient,
final LogoutRequestManager logoutRequestManager
) {
this.properties = Objects.requireNonNull(properties, "Properties required");
Expand All @@ -135,6 +140,7 @@ public OidcSecurityConfiguration(
this.jwtDecoder = Objects.requireNonNull(jwtDecoder, "JWT Decoder required");
this.idTokenDecoderFactory = Objects.requireNonNull(idTokenDecoderFactory, "ID Token Decoder Factory required");
this.oidcRestOperations = Objects.requireNonNull(oidcRestOperations, "OIDC REST Operations required");
this.oidcRestClient = Objects.requireNonNull(oidcRestClient, "OIDC Rest Client required");
this.logoutRequestManager = Objects.requireNonNull(logoutRequestManager, "Logout Request Manager required");
this.keyRotationPeriod = properties.getSecurityUserJwsKeyRotationPeriod();
}
Expand Down Expand Up @@ -207,8 +213,8 @@ public OAuth2LoginAuthenticationFilter oAuth2LoginAuthenticationFilter(final Aut
*/
@Bean
public OidcBearerTokenRefreshFilter oidcBearerTokenRefreshFilter() {
final DefaultRefreshTokenTokenResponseClient refreshTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
refreshTokenResponseClient.setRestOperations(oidcRestOperations);
final RestClientRefreshTokenTokenResponseClient refreshTokenResponseClient = new RestClientRefreshTokenTokenResponseClient();
refreshTokenResponseClient.setRestClient(oidcRestClient);

final String refreshWindowProperty = properties.getOidcTokenRefreshWindow();
final double refreshWindowSeconds = FormatUtils.getPreciseTimeDuration(refreshWindowProperty, TimeUnit.SECONDS);
Expand Down Expand Up @@ -271,9 +277,9 @@ public OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenti
*/
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
final DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRestOperations(oidcRestOperations);
return accessTokenResponseClient;
final RestClientAuthorizationCodeTokenResponseClient tokenResponseClient = new RestClientAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestClient(oidcRestClient);
return tokenResponseClient;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
import org.apache.nifi.web.security.logout.LogoutRequestManager;
import org.apache.nifi.web.security.saml2.SamlUrlPath;
import org.apache.nifi.web.security.saml2.registration.EntityDescriptorCustomizer;
import org.apache.nifi.web.security.saml2.service.authentication.ResponseAuthenticationConverter;
import org.apache.nifi.web.security.saml2.registration.Saml2RegistrationProperty;
import org.apache.nifi.web.security.saml2.service.web.StandardRelyingPartyRegistrationResolver;
Expand All @@ -44,26 +43,26 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutResponseValidator;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutResponseValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
import org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver;
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
Expand Down Expand Up @@ -222,11 +221,11 @@ public Saml2LocalLogoutFilter saml2LocalLogoutFilter() {
/**
* Spring Security OpenSAML Authentication Provider for processing SAML 2 login responses
*
* @return OpenSAML 4 Authentication Provider compatible with Java 11
* @return OpenSAML Authentication Provider
*/
@Bean
public OpenSaml4AuthenticationProvider openSamlAuthenticationProvider() {
final OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
public OpenSaml5AuthenticationProvider openSamlAuthenticationProvider() {
final OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();
final ResponseAuthenticationConverter responseAuthenticationConverter = new ResponseAuthenticationConverter(properties.getSamlGroupAttributeName());
provider.setResponseAuthenticationConverter(responseAuthenticationConverter);
return provider;
Expand All @@ -235,11 +234,11 @@ public OpenSaml4AuthenticationProvider openSamlAuthenticationProvider() {
/**
* Spring Security SAML 2 Authentication Request Resolver uses OpenSAML 4
*
* @return OpenSAML 4 version of SAML 2 Authentication Request Resolver
* @return OpenSAML SAML 2 Authentication Request Resolver
*/
@Bean
public Saml2AuthenticationRequestResolver saml2AuthenticationRequestResolver() {
return new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationResolver());
return new OpenSaml5AuthenticationRequestResolver(relyingPartyRegistrationResolver());
}

/**
Expand All @@ -249,7 +248,7 @@ public Saml2AuthenticationRequestResolver saml2AuthenticationRequestResolver() {
*/
@Bean
public Saml2LogoutRequestValidator saml2LogoutRequestValidator() {
return new OpenSamlLogoutRequestValidator();
return new OpenSaml5LogoutRequestValidator();
}

/**
Expand All @@ -259,27 +258,27 @@ public Saml2LogoutRequestValidator saml2LogoutRequestValidator() {
*/
@Bean
public Saml2LogoutResponseValidator saml2LogoutResponseValidator() {
return new OpenSamlLogoutResponseValidator();
return new OpenSaml5LogoutResponseValidator();
}

/**
* Spring Security SAML 2 Logout Request Resolver uses OpenSAML 4
*
* @return OpenSAML 4 version of SAML 2 Logout Request Resolver
* @return OpenSAML SAML 2 Logout Request Resolver
*/
@Bean
public Saml2LogoutRequestResolver saml2LogoutRequestResolver() {
return new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver());
return new OpenSaml5LogoutRequestResolver(relyingPartyRegistrationResolver());
}

/**
* Spring Security SAML 2 Logout Response Resolver uses OpenSAML 4
*
* @return OpenSAML 4 version of SAML 2 Logout Response Resolver
* @return OpenSAML SAML 2 Logout Response Resolver
*/
@Bean
public Saml2LogoutResponseResolver saml2LogoutResponseResolver() {
return new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver());
return new OpenSaml5LogoutResponseResolver(relyingPartyRegistrationResolver());
}

/**
Expand Down Expand Up @@ -326,12 +325,8 @@ public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
*/
@Bean
public Saml2MetadataResolver saml2MetadataResolver() {
final OpenSamlMetadataResolver resolver = new OpenSamlMetadataResolver();
final EntityDescriptorCustomizer customizer = new EntityDescriptorCustomizer(
properties.isSamlWantAssertionsSigned(),
properties.isSamlRequestSigningEnabled()
);
resolver.setEntityDescriptorCustomizer(customizer);
final OpenSaml5MetadataResolver resolver = new OpenSaml5MetadataResolver();
resolver.setSignMetadata(properties.isSamlRequestSigningEnabled());
return resolver;
}

Expand Down Expand Up @@ -395,9 +390,9 @@ private RelyingPartyRegistrationRepository getDisabledRelyingPartyRegistrationRe
final RelyingPartyRegistration registration = RelyingPartyRegistration
.withRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty())
.entityId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty())
.assertingPartyDetails(assertingPartyDetails -> {
assertingPartyDetails.entityId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
assertingPartyDetails.singleSignOnServiceLocation(SamlUrlPath.LOGIN_RESPONSE_REGISTRATION_ID.getPath());
.assertingPartyMetadata(assertingPartyMetadata -> {
assertingPartyMetadata.entityId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
assertingPartyMetadata.singleSignOnServiceLocation(SamlUrlPath.LOGIN_RESPONSE_REGISTRATION_ID.getPath());
})
.build();
return new InMemoryRelyingPartyRegistrationRepository(registration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Objects;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -54,15 +52,11 @@ public class StandardOidcIdTokenDecoderFactory implements JwtDecoderFactory<Clie

private static final JwsAlgorithm DEFAULT_JWS_ALGORITHM = SignatureAlgorithm.RS256;

private static final Map<JwsAlgorithm, String> SECRET_KEY_ALGORITHMS;

static {
final Map<JwsAlgorithm, String> mappings = new HashMap<>();
mappings.put(MacAlgorithm.HS256, "HmacSHA256");
mappings.put(MacAlgorithm.HS384, "HmacSHA384");
mappings.put(MacAlgorithm.HS512, "HmacSHA512");
SECRET_KEY_ALGORITHMS = Collections.unmodifiableMap(mappings);
}
private static final Map<JwsAlgorithm, String> SECRET_KEY_ALGORITHMS = Map.of(
MacAlgorithm.HS256, "HmacSHA256",
MacAlgorithm.HS384, "HmacSHA384",
MacAlgorithm.HS512, "HmacSHA512"
);

private static final ClaimTypeConverter DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ private OidcAuthorizedClient getOidcAuthorizedClient(final OAuth2AuthorizedClien
private OidcIdToken getOidcIdToken(final Authentication authentication) {
final OidcIdToken oidcIdToken;

if (authentication instanceof OAuth2AuthenticationToken) {
final OAuth2AuthenticationToken authenticationToken = (OAuth2AuthenticationToken) authentication;
if (authentication instanceof OAuth2AuthenticationToken authenticationToken) {
final OAuth2User oAuth2User = authenticationToken.getPrincipal();
if (oAuth2User instanceof OidcUser) {
final OidcUser oidcUser = (OidcUser) oAuth2User;
if (oAuth2User instanceof OidcUser oidcUser) {
oidcIdToken = oidcUser.getIdToken();
} else {
final String message = String.format("OpenID Connect User not found [%s]", oAuth2User.getClass());
Expand Down
Loading

0 comments on commit 44a18b4

Please sign in to comment.