44
44
import java .util .Map ;
45
45
import java .util .Set ;
46
46
import java .util .UUID ;
47
+ import java .util .function .Supplier ;
47
48
import java .util .stream .Stream ;
48
49
49
50
import jakarta .servlet .http .Cookie ;
68
69
import org .springframework .security .access .AccessDeniedException ;
69
70
import org .springframework .security .access .AuthorizationServiceException ;
70
71
import org .springframework .security .access .SecurityConfig ;
72
+ import org .springframework .security .access .hierarchicalroles .CycleInRoleHierarchyException ;
71
73
import org .springframework .security .access .intercept .RunAsUserToken ;
72
74
import org .springframework .security .authentication .AbstractAuthenticationToken ;
73
75
import org .springframework .security .authentication .AccountExpiredException ;
106
108
import org .springframework .security .authorization .AuthorityAuthorizationDecision ;
107
109
import org .springframework .security .authorization .AuthorizationDecision ;
108
110
import org .springframework .security .authorization .AuthorizationDeniedException ;
111
+ import org .springframework .security .authorization .event .AuthorizationEvent ;
112
+ import org .springframework .security .authorization .event .AuthorizationGrantedEvent ;
109
113
import org .springframework .security .cas .authentication .CasAssertionAuthenticationToken ;
110
114
import org .springframework .security .cas .authentication .CasAuthenticationToken ;
111
115
import org .springframework .security .cas .authentication .CasServiceTicketAuthenticationToken ;
116
+ import org .springframework .security .config .annotation .AlreadyBuiltException ;
112
117
import org .springframework .security .core .Authentication ;
113
118
import org .springframework .security .core .GrantedAuthority ;
114
119
import org .springframework .security .core .SpringSecurityCoreVersion ;
195
200
import org .springframework .security .saml2 .provider .service .authentication .Saml2RedirectAuthenticationRequest ;
196
201
import org .springframework .security .saml2 .provider .service .authentication .TestSaml2AuthenticationTokens ;
197
202
import org .springframework .security .saml2 .provider .service .authentication .TestSaml2Authentications ;
203
+ import org .springframework .security .saml2 .provider .service .authentication .TestSaml2LogoutRequests ;
198
204
import org .springframework .security .saml2 .provider .service .authentication .TestSaml2PostAuthenticationRequests ;
199
205
import org .springframework .security .saml2 .provider .service .authentication .TestSaml2RedirectAuthenticationRequests ;
206
+ import org .springframework .security .saml2 .provider .service .authentication .logout .Saml2LogoutRequest ;
200
207
import org .springframework .security .saml2 .provider .service .registration .RelyingPartyRegistration ;
201
208
import org .springframework .security .saml2 .provider .service .registration .RelyingPartyRegistration .AssertingPartyDetails ;
202
209
import org .springframework .security .saml2 .provider .service .registration .TestRelyingPartyRegistrations ;
220
227
import org .springframework .security .web .savedrequest .SimpleSavedRequest ;
221
228
import org .springframework .security .web .server .firewall .ServerExchangeRejectedException ;
222
229
import org .springframework .security .web .session .HttpSessionCreatedEvent ;
230
+ import org .springframework .security .web .session .HttpSessionIdChangedEvent ;
223
231
import org .springframework .security .web .webauthn .api .AuthenticationExtensionsClientInputs ;
224
232
import org .springframework .security .web .webauthn .api .AuthenticationExtensionsClientOutputs ;
225
233
import org .springframework .security .web .webauthn .api .AuthenticatorAssertionResponse ;
@@ -269,6 +277,8 @@ class SpringSecurityCoreVersionSerializableTests {
269
277
270
278
private static final Map <Class <?>, Generator <?>> generatorByClassName = new HashMap <>();
271
279
280
+ private static final Map <Class <?>, Supplier <InstancioApi <?>>> instancioByClassName = new HashMap <>();
281
+
272
282
static final long securitySerialVersionUid = SpringSecurityCoreVersion .SERIAL_VERSION_UID ;
273
283
274
284
static Path currentVersionFolder = Paths .get ("src/test/resources/serialized/" + getCurrentVersion ());
@@ -402,6 +412,9 @@ class SpringSecurityCoreVersionSerializableTests {
402
412
generatorByClassName .put (OAuth2IntrospectionException .class ,
403
413
(r ) -> new OAuth2IntrospectionException ("message" , new RuntimeException ()));
404
414
415
+ // config
416
+ generatorByClassName .put (AlreadyBuiltException .class , (r ) -> new AlreadyBuiltException ("message" ));
417
+
405
418
// core
406
419
generatorByClassName .put (RunAsUserToken .class , (r ) -> {
407
420
RunAsUserToken token = new RunAsUserToken ("key" , user , "creds" , user .getAuthorities (),
@@ -493,6 +506,20 @@ class SpringSecurityCoreVersionSerializableTests {
493
506
generatorByClassName .put (AuthorizationDecision .class , (r ) -> new AuthorizationDecision (true ));
494
507
generatorByClassName .put (AuthorityAuthorizationDecision .class ,
495
508
(r ) -> new AuthorityAuthorizationDecision (true , AuthorityUtils .createAuthorityList ("ROLE_USER" )));
509
+ generatorByClassName .put (CycleInRoleHierarchyException .class , (r ) -> new CycleInRoleHierarchyException ());
510
+ generatorByClassName .put (AuthorizationEvent .class ,
511
+ (r ) -> new AuthorizationEvent (new SerializableSupplier <>(authentication ), "source" ,
512
+ new AuthorizationDecision (true )));
513
+ generatorByClassName .put (AuthorizationGrantedEvent .class ,
514
+ (r ) -> new AuthorizationGrantedEvent <>(new SerializableSupplier <>(authentication ), "source" ,
515
+ new AuthorizationDecision (true )));
516
+ instancioByClassName .put (AuthorizationGrantedEvent .class , () -> {
517
+ InstancioOfClassApi <?> instancio = Instancio .of (AuthorizationGrantedEvent .class );
518
+ instancio .withTypeParameters (String .class );
519
+ instancio .supply (Select .all (AuthorizationGrantedEvent .class ),
520
+ generatorByClassName .get (AuthorizationGrantedEvent .class ));
521
+ return instancio ;
522
+ });
496
523
497
524
// cas
498
525
generatorByClassName .put (CasServiceTicketAuthenticationToken .class , (r ) -> {
@@ -546,6 +573,7 @@ class SpringSecurityCoreVersionSerializableTests {
546
573
token .setDetails (details );
547
574
return token ;
548
575
});
576
+ generatorByClassName .put (Saml2LogoutRequest .class , (r ) -> TestSaml2LogoutRequests .create ());
549
577
550
578
// web
551
579
generatorByClassName .put (AnonymousAuthenticationToken .class , (r ) -> {
@@ -602,6 +630,9 @@ class SpringSecurityCoreVersionSerializableTests {
602
630
return new SimpleSavedRequest (new DefaultSavedRequest (request , new PortResolverImpl (), "continue" ));
603
631
});
604
632
633
+ generatorByClassName .put (HttpSessionIdChangedEvent .class ,
634
+ (r ) -> new HttpSessionIdChangedEvent (new MockHttpSession (), "1" ));
635
+
605
636
// webauthn
606
637
CredProtectAuthenticationExtensionsClientInput .CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput .CredProtect (
607
638
CredProtectAuthenticationExtensionsClientInput .CredProtect .ProtectionPolicy .USER_VERIFICATION_OPTIONAL ,
@@ -670,6 +701,9 @@ class SpringSecurityCoreVersionSerializableTests {
670
701
});
671
702
// @formatter:on
672
703
704
+ generatorByClassName .put (CredentialPropertiesOutput .ExtensionOutput .class ,
705
+ (r ) -> new CredentialPropertiesOutput (true ).getOutput ());
706
+
673
707
// One-Time Token
674
708
DefaultOneTimeToken oneTimeToken = new DefaultOneTimeToken (UUID .randomUUID ().toString (), "user" ,
675
709
Instant .now ().plusSeconds (300 ));
@@ -742,7 +776,28 @@ void serializeCurrentVersionClasses(Class<?> clazz) throws Exception {
742
776
}
743
777
744
778
@ ParameterizedTest
745
- @ MethodSource ("getFilesToDeserialize" )
779
+ @ MethodSource ("getCurrentSerializedFiles" )
780
+ void shouldBeAbleToDeserializeClassFromCurrentVersion (Path filePath ) {
781
+ try (FileInputStream fileInputStream = new FileInputStream (filePath .toFile ());
782
+ ObjectInputStream objectInputStream = new ObjectInputStream (fileInputStream )) {
783
+ Object obj = objectInputStream .readObject ();
784
+ Class <?> clazz = Class .forName (filePath .getFileName ().toString ().replace (".serialized" , "" ));
785
+ assertThat (obj ).isInstanceOf (clazz );
786
+ }
787
+ catch (IOException | ClassNotFoundException ex ) {
788
+ fail ("Could not deserialize " + filePath , ex );
789
+ }
790
+ }
791
+
792
+ static Stream <Path > getCurrentSerializedFiles () throws Exception {
793
+ assertThat (currentVersionFolder .toFile ().exists ())
794
+ .as ("Make sure that the " + currentVersionFolder + " exists and is not empty" )
795
+ .isTrue ();
796
+ return getClassesToSerialize ().map ((clazz ) -> currentVersionFolder .resolve (clazz .getName () + ".serialized" ));
797
+ }
798
+
799
+ @ ParameterizedTest
800
+ @ MethodSource ("getPreviousSerializedFiles" )
746
801
void shouldBeAbleToDeserializeClassFromPreviousVersion (Path filePath ) {
747
802
try (FileInputStream fileInputStream = new FileInputStream (filePath .toFile ());
748
803
ObjectInputStream objectInputStream = new ObjectInputStream (fileInputStream )) {
@@ -755,7 +810,7 @@ void shouldBeAbleToDeserializeClassFromPreviousVersion(Path filePath) {
755
810
}
756
811
}
757
812
758
- static Stream <Path > getFilesToDeserialize () throws IOException {
813
+ static Stream <Path > getPreviousSerializedFiles () throws IOException {
759
814
assertThat (previousVersionFolder .toFile ().exists ())
760
815
.as ("Make sure that the " + previousVersionFolder + " exists and is not empty" )
761
816
.isTrue ();
@@ -791,10 +846,18 @@ void allSerializableClassesShouldHaveSerialVersionOrSuppressWarnings() throws Ex
791
846
|| Arrays .asList (suppressWarnings .value ()).contains ("Serial" );
792
847
if (!hasSerialVersion && !hasSerialIgnore ) {
793
848
classes .add (clazz );
849
+ continue ;
850
+ }
851
+ boolean isReachable = Modifier .isPublic (clazz .getModifiers ());
852
+ boolean hasSampleSerialization = currentVersionFolder .resolve (clazz .getName () + ".serialized" )
853
+ .toFile ()
854
+ .exists ();
855
+ if (hasSerialVersion && isReachable && !hasSampleSerialization ) {
856
+ classes .add (clazz );
794
857
}
795
858
}
796
- assertThat (classes )
797
- . describedAs ( "Found Serializable classes that are either missing a serialVersionUID or a @SuppressWarnings" )
859
+ assertThat (classes ). describedAs (
860
+ "Found Serializable classes that are either missing a serialVersionUID or a @SuppressWarnings or a sample serialized file " )
798
861
.isEmpty ();
799
862
}
800
863
@@ -821,6 +884,9 @@ static Stream<Class<?>> getClassesToSerialize() throws Exception {
821
884
}
822
885
823
886
private static InstancioApi <?> instancioWithDefaults (Class <?> clazz ) {
887
+ if (instancioByClassName .containsKey (clazz )) {
888
+ return instancioByClassName .get (clazz ).get ();
889
+ }
824
890
InstancioOfClassApi <?> instancio = Instancio .of (clazz );
825
891
ResolvableType [] generics = ResolvableType .forClass (clazz ).getGenerics ();
826
892
for (ResolvableType type : generics ) {
@@ -853,4 +919,20 @@ private static String getPreviousVersion() {
853
919
return String .join ("." , parts );
854
920
}
855
921
922
+ @ SuppressWarnings ("serial" )
923
+ private static final class SerializableSupplier <T > implements Supplier <T >, Serializable {
924
+
925
+ private final T value ;
926
+
927
+ SerializableSupplier (T value ) {
928
+ this .value = value ;
929
+ }
930
+
931
+ @ Override
932
+ public T get () {
933
+ return this .value ;
934
+ }
935
+
936
+ }
937
+
856
938
}
0 commit comments