From 2b9efccc501e563161c11500d55027d32522502a Mon Sep 17 00:00:00 2001 From: Arnaud Mergey Date: Mon, 26 Oct 2020 18:48:59 +0100 Subject: [PATCH] Implement MessageSourceAware where missing Closes gh-8951 --- ...rDetailsReactiveAuthenticationManager.java | 16 +++++++++++-- ...oryReactiveAuthenticationManagerTests.java | 19 ++++++++++++++- .../access/ExceptionTranslationFilter.java | 17 ++++++++++--- .../x509/SubjectDnX509PrincipalExtractor.java | 10 ++++++-- .../AbstractRememberMeServices.java | 18 +++++++++++--- .../ExceptionTranslationFilterTests.java | 21 +++++++++++++++- .../SubjectDnX509PrincipalExtractorTests.java | 23 +++++++++++++++--- .../AbstractRememberMeServicesTests.java | 24 ++++++++++++++++++- 8 files changed, 132 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java b/core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java index 4864ea12ee..d75a2775cd 100644 --- a/core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java +++ b/core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.core.Authentication; import org.springframework.security.core.SpringSecurityMessageSource; @@ -45,7 +47,8 @@ import org.springframework.util.Assert; * @author EddĂș MelĂ©ndez * @since 5.2 */ -public abstract class AbstractUserDetailsReactiveAuthenticationManager implements ReactiveAuthenticationManager { +public abstract class AbstractUserDetailsReactiveAuthenticationManager + implements ReactiveAuthenticationManager, MessageSourceAware { protected final Log logger = LogFactory.getLog(getClass()); @@ -164,6 +167,15 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager implement this.postAuthenticationChecks = postAuthenticationChecks; } + /** + * @since 5.5 + */ + @Override + public void setMessageSource(MessageSource messageSource) { + Assert.notNull(messageSource, "messageSource cannot be null"); + this.messages = new MessageSourceAccessor(messageSource); + } + /** * Allows subclasses to retrieve the UserDetails from an * implementation-specific location. diff --git a/core/src/test/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManagerTests.java b/core/src/test/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManagerTests.java index 06630e17f3..72d05fa063 100644 --- a/core/src/test/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; +import org.springframework.context.MessageSource; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService; import org.springframework.security.core.userdetails.ReactiveUserDetailsService; @@ -34,10 +35,12 @@ import org.springframework.security.core.userdetails.UserDetailsChecker; import org.springframework.security.crypto.password.PasswordEncoder; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -214,4 +217,18 @@ public class UserDetailsRepositoryReactiveAuthenticationManagerTests { assertThatExceptionOfType(DisabledException.class).isThrownBy(() -> this.manager.authenticate(token).block()); } + @Test + public void setMessageSourceWhenNullThenThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.manager.setMessageSource(null)); + } + + @Test + public void setMessageSourceWhenNotNullThenCanGet() { + MessageSource source = mock(MessageSource.class); + this.manager.setMessageSource(source); + String code = "code"; + this.manager.messages.getMessage(code); + verify(source).getMessage(eq(code), any(), any()); + } + } diff --git a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java index ae7a0367b4..e12bbb53c9 100644 --- a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java +++ b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2016 the original author or authors. + * Copyright 2004-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.log.LogMessage; import org.springframework.security.access.AccessDeniedException; @@ -77,7 +79,7 @@ import org.springframework.web.filter.GenericFilterBean; * @author Ben Alex * @author colin sampaleanu */ -public class ExceptionTranslationFilter extends GenericFilterBean { +public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware { private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); @@ -89,7 +91,7 @@ public class ExceptionTranslationFilter extends GenericFilterBean { private RequestCache requestCache = new HttpSessionRequestCache(); - private final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint) { this(authenticationEntryPoint, new HttpSessionRequestCache()); @@ -226,6 +228,15 @@ public class ExceptionTranslationFilter extends GenericFilterBean { this.throwableAnalyzer = throwableAnalyzer; } + /** + * @since 5.5 + */ + @Override + public void setMessageSource(MessageSource messageSource) { + Assert.notNull(messageSource, "messageSource cannot be null"); + this.messages = new MessageSourceAccessor(messageSource); + } + /** * Default implementation of ThrowableAnalyzer which is capable of also * unwrapping ServletExceptions. diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractor.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractor.java index 56e5c33b9e..b690af59c0 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractor.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.log.LogMessage; import org.springframework.security.authentication.BadCredentialsException; @@ -43,7 +44,7 @@ import org.springframework.util.Assert; * * @author Luke Taylor */ -public class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor { +public class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor, MessageSourceAware { protected final Log logger = LogFactory.getLog(getClass()); @@ -88,7 +89,12 @@ public class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor { this.subjectDnPattern = Pattern.compile(subjectDnRegex, Pattern.CASE_INSENSITIVE); } + /** + * @since 5.5 + */ + @Override public void setMessageSource(MessageSource messageSource) { + Assert.notNull(messageSource, "messageSource cannot be null"); this.messages = new MessageSourceAccessor(messageSource); } diff --git a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java index 1d25067d37..b869316c74 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java +++ b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.log.LogMessage; import org.springframework.security.authentication.AccountStatusException; @@ -59,7 +61,8 @@ import org.springframework.util.StringUtils; * @author Onur Kagan Ozcan * @since 2.0 */ -public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler { +public abstract class AbstractRememberMeServices + implements RememberMeServices, InitializingBean, LogoutHandler, MessageSourceAware { public static final String SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY = "remember-me"; @@ -71,7 +74,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, protected final Log logger = LogFactory.getLog(getClass()); - protected final MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private UserDetailsService userDetailsService; @@ -481,4 +484,13 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, this.authoritiesMapper = authoritiesMapper; } + /** + * @since 5.5 + */ + @Override + public void setMessageSource(MessageSource messageSource) { + Assert.notNull(messageSource, "messageSource cannot be null"); + this.messages = new MessageSourceAccessor(messageSource); + } + } diff --git a/web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java b/web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java index 77b3560480..3269e20cb1 100644 --- a/web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2016 the original author or authors. + * Copyright 2004-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -50,8 +51,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** @@ -284,6 +287,22 @@ public class ExceptionTranslationFilterTests { verifyZeroInteractions(this.mockEntryPoint); } + @Test + public void setMessageSourceWhenNullThenThrowsException() { + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint); + assertThatIllegalArgumentException().isThrownBy(() -> filter.setMessageSource(null)); + } + + @Test + public void setMessageSourceWhenNotNullThenCanGet() { + MessageSource source = mock(MessageSource.class); + ExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint); + filter.setMessageSource(source); + String code = "code"; + filter.messages.getMessage(code); + verify(source).getMessage(eq(code), any(), any()); + } + private AuthenticationEntryPoint mockEntryPoint = (request, response, authException) -> response .sendRedirect(request.getContextPath() + "/login.jsp"); diff --git a/web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractorTests.java b/web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractorTests.java index 11c92891a1..5519e6ff1b 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractorTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,16 @@ package org.springframework.security.web.authentication.preauth.x509; import org.junit.Before; import org.junit.Test; +import org.springframework.context.MessageSource; import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.SpringSecurityMessageSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * @author Luke Taylor @@ -36,7 +40,6 @@ public class SubjectDnX509PrincipalExtractorTests { @Before public void setUp() { this.extractor = new SubjectDnX509PrincipalExtractor(); - this.extractor.setMessageSource(new SpringSecurityMessageSource()); } @Test @@ -71,4 +74,18 @@ public class SubjectDnX509PrincipalExtractorTests { assertThat(principal).isEqualTo("Duke"); } + @Test + public void setMessageSourceWhenNullThenThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.extractor.setMessageSource(null)); + } + + @Test + public void setMessageSourceWhenNotNullThenCanGet() { + MessageSource source = mock(MessageSource.class); + this.extractor.setMessageSource(source); + String code = "code"; + this.extractor.messages.getMessage(code); + verify(source).getMessage(eq(code), any(), any()); + } + } diff --git a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java index 038c3cd163..a0be9eec1a 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.context.MessageSource; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AccountStatusUserDetailsChecker; @@ -44,6 +45,11 @@ import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * @author Luke Taylor @@ -411,6 +417,22 @@ public class AbstractRememberMeServicesTests { assertThat(cookie.getDomain()).isEqualTo("spring.io"); } + @Test + public void setMessageSourceWhenNullThenThrowsException() { + MockRememberMeServices services = new MockRememberMeServices(); + assertThatIllegalArgumentException().isThrownBy(() -> services.setMessageSource(null)); + } + + @Test + public void setMessageSourceWhenNotNullThenCanGet() { + MessageSource source = mock(MessageSource.class); + MockRememberMeServices services = new MockRememberMeServices(); + services.setMessageSource(source); + String code = "code"; + services.messages.getMessage(code); + verify(source).getMessage(eq(code), any(), any()); + } + private Cookie[] createLoginCookie(String cookieToken) { MockRememberMeServices services = new MockRememberMeServices(this.uds); Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,