Josh Cummings пре 3 месеци
родитељ
комит
34a9f57aa6

+ 86 - 4
config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java

@@ -44,6 +44,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import jakarta.servlet.http.Cookie;
@@ -68,6 +69,7 @@ import org.springframework.mock.web.MockHttpSession;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.access.AuthorizationServiceException;
 import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.access.hierarchicalroles.CycleInRoleHierarchyException;
 import org.springframework.security.access.intercept.RunAsUserToken;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.authentication.AccountExpiredException;
@@ -106,9 +108,12 @@ import org.springframework.security.authentication.password.CompromisedPasswordE
 import org.springframework.security.authorization.AuthorityAuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationDeniedException;
+import org.springframework.security.authorization.event.AuthorizationEvent;
+import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
 import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
 import org.springframework.security.cas.authentication.CasAuthenticationToken;
 import org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;
+import org.springframework.security.config.annotation.AlreadyBuiltException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.SpringSecurityCoreVersion;
@@ -195,8 +200,10 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2P
 import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
 import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;
 import org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;
+import org.springframework.security.saml2.provider.service.authentication.TestSaml2LogoutRequests;
 import org.springframework.security.saml2.provider.service.authentication.TestSaml2PostAuthenticationRequests;
 import org.springframework.security.saml2.provider.service.authentication.TestSaml2RedirectAuthenticationRequests;
+import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails;
 import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
@@ -220,6 +227,7 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest;
 import org.springframework.security.web.savedrequest.SimpleSavedRequest;
 import org.springframework.security.web.server.firewall.ServerExchangeRejectedException;
 import org.springframework.security.web.session.HttpSessionCreatedEvent;
+import org.springframework.security.web.session.HttpSessionIdChangedEvent;
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
 import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
@@ -269,6 +277,8 @@ class SpringSecurityCoreVersionSerializableTests {
 
 	private static final Map<Class<?>, Generator<?>> generatorByClassName = new HashMap<>();
 
+	private static final Map<Class<?>, Supplier<InstancioApi<?>>> instancioByClassName = new HashMap<>();
+
 	static final long securitySerialVersionUid = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
 
 	static Path currentVersionFolder = Paths.get("src/test/resources/serialized/" + getCurrentVersion());
@@ -402,6 +412,9 @@ class SpringSecurityCoreVersionSerializableTests {
 		generatorByClassName.put(OAuth2IntrospectionException.class,
 				(r) -> new OAuth2IntrospectionException("message", new RuntimeException()));
 
+		// config
+		generatorByClassName.put(AlreadyBuiltException.class, (r) -> new AlreadyBuiltException("message"));
+
 		// core
 		generatorByClassName.put(RunAsUserToken.class, (r) -> {
 			RunAsUserToken token = new RunAsUserToken("key", user, "creds", user.getAuthorities(),
@@ -493,6 +506,20 @@ class SpringSecurityCoreVersionSerializableTests {
 		generatorByClassName.put(AuthorizationDecision.class, (r) -> new AuthorizationDecision(true));
 		generatorByClassName.put(AuthorityAuthorizationDecision.class,
 				(r) -> new AuthorityAuthorizationDecision(true, AuthorityUtils.createAuthorityList("ROLE_USER")));
+		generatorByClassName.put(CycleInRoleHierarchyException.class, (r) -> new CycleInRoleHierarchyException());
+		generatorByClassName.put(AuthorizationEvent.class,
+				(r) -> new AuthorizationEvent(new SerializableSupplier<>(authentication), "source",
+						new AuthorizationDecision(true)));
+		generatorByClassName.put(AuthorizationGrantedEvent.class,
+				(r) -> new AuthorizationGrantedEvent<>(new SerializableSupplier<>(authentication), "source",
+						new AuthorizationDecision(true)));
+		instancioByClassName.put(AuthorizationGrantedEvent.class, () -> {
+			InstancioOfClassApi<?> instancio = Instancio.of(AuthorizationGrantedEvent.class);
+			instancio.withTypeParameters(String.class);
+			instancio.supply(Select.all(AuthorizationGrantedEvent.class),
+					generatorByClassName.get(AuthorizationGrantedEvent.class));
+			return instancio;
+		});
 
 		// cas
 		generatorByClassName.put(CasServiceTicketAuthenticationToken.class, (r) -> {
@@ -546,6 +573,7 @@ class SpringSecurityCoreVersionSerializableTests {
 			token.setDetails(details);
 			return token;
 		});
+		generatorByClassName.put(Saml2LogoutRequest.class, (r) -> TestSaml2LogoutRequests.create());
 
 		// web
 		generatorByClassName.put(AnonymousAuthenticationToken.class, (r) -> {
@@ -602,6 +630,9 @@ class SpringSecurityCoreVersionSerializableTests {
 			return new SimpleSavedRequest(new DefaultSavedRequest(request, new PortResolverImpl(), "continue"));
 		});
 
+		generatorByClassName.put(HttpSessionIdChangedEvent.class,
+				(r) -> new HttpSessionIdChangedEvent(new MockHttpSession(), "1"));
+
 		// webauthn
 		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
 				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,
@@ -670,6 +701,9 @@ class SpringSecurityCoreVersionSerializableTests {
 		});
 		// @formatter:on
 
+		generatorByClassName.put(CredentialPropertiesOutput.ExtensionOutput.class,
+				(r) -> new CredentialPropertiesOutput(true).getOutput());
+
 		// One-Time Token
 		DefaultOneTimeToken oneTimeToken = new DefaultOneTimeToken(UUID.randomUUID().toString(), "user",
 				Instant.now().plusSeconds(300));
@@ -742,7 +776,28 @@ class SpringSecurityCoreVersionSerializableTests {
 	}
 
 	@ParameterizedTest
-	@MethodSource("getFilesToDeserialize")
+	@MethodSource("getCurrentSerializedFiles")
+	void shouldBeAbleToDeserializeClassFromCurrentVersion(Path filePath) {
+		try (FileInputStream fileInputStream = new FileInputStream(filePath.toFile());
+				ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
+			Object obj = objectInputStream.readObject();
+			Class<?> clazz = Class.forName(filePath.getFileName().toString().replace(".serialized", ""));
+			assertThat(obj).isInstanceOf(clazz);
+		}
+		catch (IOException | ClassNotFoundException ex) {
+			fail("Could not deserialize " + filePath, ex);
+		}
+	}
+
+	static Stream<Path> getCurrentSerializedFiles() throws Exception {
+		assertThat(currentVersionFolder.toFile().exists())
+			.as("Make sure that the " + currentVersionFolder + " exists and is not empty")
+			.isTrue();
+		return getClassesToSerialize().map((clazz) -> currentVersionFolder.resolve(clazz.getName() + ".serialized"));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getPreviousSerializedFiles")
 	void shouldBeAbleToDeserializeClassFromPreviousVersion(Path filePath) {
 		try (FileInputStream fileInputStream = new FileInputStream(filePath.toFile());
 				ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
@@ -755,7 +810,7 @@ class SpringSecurityCoreVersionSerializableTests {
 		}
 	}
 
-	static Stream<Path> getFilesToDeserialize() throws IOException {
+	static Stream<Path> getPreviousSerializedFiles() throws IOException {
 		assertThat(previousVersionFolder.toFile().exists())
 			.as("Make sure that the " + previousVersionFolder + " exists and is not empty")
 			.isTrue();
@@ -791,10 +846,18 @@ class SpringSecurityCoreVersionSerializableTests {
 					|| Arrays.asList(suppressWarnings.value()).contains("Serial");
 			if (!hasSerialVersion && !hasSerialIgnore) {
 				classes.add(clazz);
+				continue;
+			}
+			boolean isReachable = Modifier.isPublic(clazz.getModifiers());
+			boolean hasSampleSerialization = currentVersionFolder.resolve(clazz.getName() + ".serialized")
+				.toFile()
+				.exists();
+			if (hasSerialVersion && isReachable && !hasSampleSerialization) {
+				classes.add(clazz);
 			}
 		}
-		assertThat(classes)
-			.describedAs("Found Serializable classes that are either missing a serialVersionUID or a @SuppressWarnings")
+		assertThat(classes).describedAs(
+				"Found Serializable classes that are either missing a serialVersionUID or a @SuppressWarnings or a sample serialized file")
 			.isEmpty();
 	}
 
@@ -821,6 +884,9 @@ class SpringSecurityCoreVersionSerializableTests {
 	}
 
 	private static InstancioApi<?> instancioWithDefaults(Class<?> clazz) {
+		if (instancioByClassName.containsKey(clazz)) {
+			return instancioByClassName.get(clazz).get();
+		}
 		InstancioOfClassApi<?> instancio = Instancio.of(clazz);
 		ResolvableType[] generics = ResolvableType.forClass(clazz).getGenerics();
 		for (ResolvableType type : generics) {
@@ -853,4 +919,20 @@ class SpringSecurityCoreVersionSerializableTests {
 		return String.join(".", parts);
 	}
 
+	@SuppressWarnings("serial")
+	private static final class SerializableSupplier<T> implements Supplier<T>, Serializable {
+
+		private final T value;
+
+		SerializableSupplier(T value) {
+			this.value = value;
+		}
+
+		@Override
+		public T get() {
+			return this.value;
+		}
+
+	}
+
 }

+ 1 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/Authz.java

@@ -55,6 +55,7 @@ public class Authz {
 		return Mono.just(checkResult(result));
 	}
 
+	@SuppressWarnings("serial")
 	public static class AuthzResult extends AuthorizationDecision {
 
 		public AuthzResult(boolean granted) {

BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.access.hierarchicalroles.CycleInRoleHierarchyException.serialized


BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.authorization.event.AuthorizationEvent.serialized


BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.authorization.event.AuthorizationGrantedEvent.serialized


BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.config.annotation.AlreadyBuiltException.serialized


BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.web.session.HttpSessionIdChangedEvent.serialized


BIN
config/src/test/resources/serialized/6.4.x/org.springframework.security.web.webauthn.api.CredentialPropertiesOutput$ExtensionOutput.serialized


+ 1 - 4
core/src/test/java/org/springframework/security/access/annotation/BusinessServiceImpl.java

@@ -16,18 +16,15 @@
 
 package org.springframework.security.access.annotation;
 
-import java.io.Serial;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * @author Joe Scalise
  */
+@SuppressWarnings("serial")
 public class BusinessServiceImpl<E extends Entity> implements BusinessService {
 
-	@Serial
-	private static final long serialVersionUID = -4249394090237180795L;
-
 	@Override
 	@Secured({ "ROLE_USER" })
 	public void someUserMethod1() {

+ 1 - 4
core/src/test/java/org/springframework/security/access/annotation/ExpressionProtectedBusinessServiceImpl.java

@@ -16,7 +16,6 @@
 
 package org.springframework.security.access.annotation;
 
-import java.io.Serial;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -24,11 +23,9 @@ import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreFilter;
 
+@SuppressWarnings("serial")
 public class ExpressionProtectedBusinessServiceImpl implements BusinessService {
 
-	@Serial
-	private static final long serialVersionUID = -3320014879907436606L;
-
 	@Override
 	public void someAdminMethod() {
 	}

+ 1 - 4
core/src/test/java/org/springframework/security/access/annotation/Jsr250BusinessServiceImpl.java

@@ -16,7 +16,6 @@
 
 package org.springframework.security.access.annotation;
 
-import java.io.Serial;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,11 +26,9 @@ import jakarta.annotation.security.RolesAllowed;
  * @author Luke Taylor
  */
 @PermitAll
+@SuppressWarnings("serial")
 public class Jsr250BusinessServiceImpl implements BusinessService {
 
-	@Serial
-	private static final long serialVersionUID = -7550211450382764339L;
-
 	@Override
 	@RolesAllowed("ROLE_USER")
 	public void someUserMethod1() {