Forráskód Böngészése

Fix ProviderManager.copyDetails Changes Authentication Type

Closes gh-18027
Rob Winch 1 napja
szülő
commit
864a9b2fb3

+ 4 - 8
core/src/main/java/org/springframework/security/authentication/ProviderManager.java

@@ -182,7 +182,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
 			try {
 				result = provider.authenticate(authentication);
 				if (result != null) {
-					result = copyDetails(authentication, result);
+					copyDetails(authentication, result);
 					break;
 				}
 			}
@@ -277,14 +277,10 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
 	 * @param source source authentication
 	 * @param dest the destination authentication object
 	 */
-	private Authentication copyDetails(Authentication source, Authentication dest) {
-		if (source.getDetails() == null) {
-			return dest;
+	private void copyDetails(Authentication source, Authentication dest) {
+		if ((dest instanceof AbstractAuthenticationToken token) && (dest.getDetails() == null)) {
+			token.setDetails(source.getDetails());
 		}
-		if (dest.getDetails() != null) {
-			return dest;
-		}
-		return dest.toBuilder().details(source.getDetails()).build();
 	}
 
 	public List<AuthenticationProvider> getProviders() {

+ 59 - 0
core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java

@@ -21,6 +21,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.context.MessageSource;
@@ -162,6 +163,20 @@ public class ProviderManagerTests {
 		assertThat(result.getDetails()).isSameAs(details);
 	}
 
+	// gh-18027
+	@Test
+	void authenticationIsSameWhenDetailsSetAndAuthenticationToBuilderIsDefault() {
+		Authentication customAuthentication = new DefaultToBuilderAuthentication();
+		AuthenticationProvider provider = mock(AuthenticationProvider.class);
+		given(provider.supports(any())).willReturn(true);
+		given(provider.authenticate(any())).willReturn(customAuthentication);
+		TestingAuthenticationToken request = createAuthenticationToken();
+		request.setDetails(new Object());
+		ProviderManager authMgr = new ProviderManager(provider);
+		Authentication result = authMgr.authenticate(request);
+		assertThat(result).isSameAs(customAuthentication);
+	}
+
 	@Test
 	void authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() {
 		Authentication result = new TestingAuthenticationToken("user", "pass", "FACTOR");
@@ -356,4 +371,48 @@ public class ProviderManagerTests {
 
 	}
 
+	/**
+	 * Represents a custom {@link Authentication} that does not override
+	 * {@link #toBuilder()}. We should remain passive to previous versions of Spring
+	 * Security and not change the {@link Authentication} type.
+	 */
+	private static final class DefaultToBuilderAuthentication implements Authentication {
+
+		@Override
+		public Collection<? extends GrantedAuthority> getAuthorities() {
+			return List.of();
+		}
+
+		@Override
+		public @Nullable Object getCredentials() {
+			return null;
+		}
+
+		@Override
+		public @Nullable Object getDetails() {
+			return null;
+		}
+
+		@Override
+		public @Nullable Object getPrincipal() {
+			return null;
+		}
+
+		@Override
+		public boolean isAuthenticated() {
+			return false;
+		}
+
+		@Override
+		public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
+
+		}
+
+		@Override
+		public String getName() {
+			return "";
+		}
+
+	}
+
 }