diff --git a/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java b/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java index 478e989..c123b0f 100644 --- a/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java +++ b/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java @@ -9,7 +9,6 @@ import org.springframework.context.annotation.Profile; import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.security.config.Customizer; -import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -24,10 +23,8 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.intercept.AuthorizationFilter; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter; -import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; import se.su.dsv.oauth2.shibboleth.Entitlement; -import se.su.dsv.oauth2.shibboleth.ShibbolethAuthenticationDetailsSource; import se.su.dsv.oauth2.shibboleth.ShibbolethConfigurer; import se.su.dsv.oauth2.shibboleth.ShibbolethTokenPopulator; import se.su.dsv.oauth2.staging.StagingSecurityConfigurer; @@ -140,30 +137,11 @@ public class AuthorizationServer extends SpringBootServletInitializer { .accessDeniedPage("/forbidden") .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))); - http.jee(jee -> jee - .withObjectPostProcessor(makeShibbolethAware())); + http.with(new ShibbolethConfigurer(), Customizer.withDefaults()); return http.build(); } - private static ObjectPostProcessor<J2eePreAuthenticatedProcessingFilter> makeShibbolethAware() - { - return new ObjectPostProcessor<>() { - @Override - public <O extends J2eePreAuthenticatedProcessingFilter> O postProcess(final O object) { - // Using a custom authentication details source to extract the Shibboleth attributes - // and convert them to the relevant Spring Security objects. - object.setAuthenticationDetailsSource(new ShibbolethAuthenticationDetailsSource()); - - // Prevent session creation - // It can cause conflicts when running on the same host as an embedded docker container - // as it overwrites the session cookie (it does not factor in port) - object.setSecurityContextRepository(new RequestAttributeSecurityContextRepository()); - return object; - } - }; - } - @Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() { return new ShibbolethTokenPopulator(); diff --git a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java new file mode 100644 index 0000000..b642843 --- /dev/null +++ b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java @@ -0,0 +1,29 @@ +package se.su.dsv.oauth2.shibboleth; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.log.LogMessage; +import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter; + +import java.security.Principal; + +/// Can't use the built-in [J2eePreAuthenticatedProcessingFilter] because Tomcat +/// unconditionally provides a [HttpServletRequest#getUserPrincipal()] with a +/// blank [Principal#getName()] that causes the authentication process to fail. +class ShibbolethAuthenticatedProcessingFilter extends J2eePreAuthenticatedProcessingFilter { + @Override + protected Object getPreAuthenticatedPrincipal(final HttpServletRequest httpRequest) { + Principal userPrincipal = httpRequest.getUserPrincipal(); + if (userPrincipal == null) { + return null; + } + + // Tomcat provides a blank Principal name when the user is not authenticated. + String principalName = userPrincipal.getName(); + if (principalName == null || principalName.isBlank()) { + return null; + } + + this.logger.debug(LogMessage.format("PreAuthenticated J2EE principal: %s", principalName)); + return principalName; + } +} diff --git a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java index 1aa0252..5a9a511 100644 --- a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java +++ b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java @@ -20,7 +20,7 @@ public class ShibbolethConfigurer extends AbstractHttpConfigurer<ShibbolethConfi @Override public void configure(final HttpSecurity http) { - J2eePreAuthenticatedProcessingFilter filter = new J2eePreAuthenticatedProcessingFilter(); + J2eePreAuthenticatedProcessingFilter filter = new ShibbolethAuthenticatedProcessingFilter(); filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); filter.setAuthenticationDetailsSource(new ShibbolethAuthenticationDetailsSource()); filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());