From e8fd90aaea6818ac528295bbddf4c429500200fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sun, 24 Nov 2024 12:08:31 +0100 Subject: [PATCH] fix(rest-jackson,security): improve @SecureField annotation detection --- docs/src/main/asciidoc/rest.adoc | 3 + .../ResteasyReactiveJacksonProcessor.java | 50 ++++++++-- .../test/DisableSecureSerializationTest.java | 94 +++++++++++++++++++ .../jackson/deployment/test/Fruit.java | 16 ++++ .../deployment/test/GenericWrapper.java | 14 +++ .../jackson/deployment/test/Price.java | 17 ++++ .../deployment/test/SimpleJsonResource.java | 34 +++---- .../deployment/test/SimpleJsonTest.java | 23 ++++- ...JsonWithReflectionFreeSerializersTest.java | 12 ++- 9 files changed, 230 insertions(+), 33 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/DisableSecureSerializationTest.java create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Fruit.java create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/GenericWrapper.java create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Price.java diff --git a/docs/src/main/asciidoc/rest.adoc b/docs/src/main/asciidoc/rest.adoc index aa0ffb3752396..c3ffbf337e6d9 100644 --- a/docs/src/main/asciidoc/rest.adoc +++ b/docs/src/main/asciidoc/rest.adoc @@ -1535,6 +1535,9 @@ WARNING: Currently you cannot use the `@SecureField` annotation to secure your d ==== All resource methods returning data secured with the `@SecureField` annotation should be tested. Please make sure data are secured as you intended. +Quarkus always attempts to detect fields annotated with the `@SecureField` annotation, +however it may fail to infer returned type and miss the `@SecureField` annotation instance. +If that happens, please explicitly enable secure serialization on the resource endpoint with the `@EnableSecureSerialization` annotation. ==== Assuming security has been set up for the application (see our xref:security-overview.adoc[guide] for more details), when a user with the `admin` role diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java index 43ef5caad8bea..b8607985a1bb8 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -455,6 +455,8 @@ public void handleFieldSecurity(ResteasyReactiveResourceMethodEntriesBuildItem r continue; } } + boolean secureSerializationExplicitlyEnabled = methodInfo.hasAnnotation(ENABLE_SECURE_SERIALIZATION) + || entry.getActualClassInfo().hasDeclaredAnnotation(ENABLE_SECURE_SERIALIZATION); ResourceMethod resourceInfo = entry.getResourceMethod(); boolean isJsonResponse = false; @@ -470,12 +472,31 @@ public void handleFieldSecurity(ResteasyReactiveResourceMethodEntriesBuildItem r continue; } - ClassInfo effectiveReturnClassInfo = getEffectiveClassInfo(methodInfo.returnType(), indexView); + var methodReturnType = methodInfo.returnType(); + ClassInfo effectiveReturnClassInfo = getEffectiveClassInfo(methodReturnType, indexView); if (effectiveReturnClassInfo == null) { continue; } + + final Map typeParamIdentifierToParameterizedType; + if (methodReturnType.kind() == Type.Kind.PARAMETERIZED_TYPE) { + typeParamIdentifierToParameterizedType = new HashMap<>(); + var parametrizedReturnType = methodReturnType.asParameterizedType(); + for (int i = 0; i < parametrizedReturnType.arguments().size(); i++) { + if (i < effectiveReturnClassInfo.typeParameters().size()) { + var identifier = effectiveReturnClassInfo.typeParameters().get(i).identifier(); + var parametrizedTypeArg = parametrizedReturnType.arguments().get(i); + typeParamIdentifierToParameterizedType.put(identifier, parametrizedTypeArg); + } + } + } else { + typeParamIdentifierToParameterizedType = null; + } + AtomicBoolean needToDeleteCache = new AtomicBoolean(false); - if (hasSecureFields(indexView, effectiveReturnClassInfo, typeToHasSecureField, needToDeleteCache)) { + if (secureSerializationExplicitlyEnabled + || hasSecureFields(indexView, effectiveReturnClassInfo, typeToHasSecureField, needToDeleteCache, + typeParamIdentifierToParameterizedType)) { AnnotationInstance customSerializationAtClassAnnotation = methodInfo.declaringClass() .declaredAnnotation(CUSTOM_SERIALIZATION); AnnotationInstance customSerializationAtMethodAnnotation = methodInfo.annotation(CUSTOM_SERIALIZATION); @@ -539,7 +560,8 @@ private static Map getTypesWithSecureField() { } private static boolean hasSecureFields(IndexView indexView, ClassInfo currentClassInfo, - Map typeToHasSecureField, AtomicBoolean needToDeleteCache) { + Map typeToHasSecureField, AtomicBoolean needToDeleteCache, + Map typeParamIdentifierToParameterizedType) { // use cached result if there is any final String className = currentClassInfo.name().toString(); if (typeToHasSecureField.containsKey(className)) { @@ -565,7 +587,7 @@ private static boolean hasSecureFields(IndexView indexView, ClassInfo currentCla } else { // check interface implementors as anyone of them can be returned hasSecureFields = indexView.getAllKnownImplementors(currentClassInfo.name()).stream() - .anyMatch(ci -> hasSecureFields(indexView, ci, typeToHasSecureField, needToDeleteCache)); + .anyMatch(ci -> hasSecureFields(indexView, ci, typeToHasSecureField, needToDeleteCache, null)); } } else { // figure if any field or parent / subclass field is secured @@ -576,7 +598,7 @@ private static boolean hasSecureFields(IndexView indexView, ClassInfo currentCla hasSecureFields = false; } else { hasSecureFields = anyFieldHasSecureFields(indexView, currentClassInfo, typeToHasSecureField, - needToDeleteCache) + needToDeleteCache, typeParamIdentifierToParameterizedType) || anySubclassHasSecureFields(indexView, currentClassInfo, typeToHasSecureField, needToDeleteCache) || anyParentClassHasSecureFields(indexView, currentClassInfo, typeToHasSecureField, needToDeleteCache); @@ -600,7 +622,7 @@ private static boolean anyParentClassHasSecureFields(IndexView indexView, ClassI if (!currentClassInfo.superName().equals(ResteasyReactiveDotNames.OBJECT)) { final ClassInfo parentClassInfo = indexView.getClassByName(currentClassInfo.superName()); return parentClassInfo != null - && hasSecureFields(indexView, parentClassInfo, typeToHasSecureField, needToDeleteCache); + && hasSecureFields(indexView, parentClassInfo, typeToHasSecureField, needToDeleteCache, null); } return false; } @@ -608,16 +630,26 @@ private static boolean anyParentClassHasSecureFields(IndexView indexView, ClassI private static boolean anySubclassHasSecureFields(IndexView indexView, ClassInfo currentClassInfo, Map typeToHasSecureField, AtomicBoolean needToDeleteCache) { return indexView.getAllKnownSubclasses(currentClassInfo.name()).stream() - .anyMatch(subclass -> hasSecureFields(indexView, subclass, typeToHasSecureField, needToDeleteCache)); + .anyMatch(subclass -> hasSecureFields(indexView, subclass, typeToHasSecureField, needToDeleteCache, null)); } private static boolean anyFieldHasSecureFields(IndexView indexView, ClassInfo currentClassInfo, - Map typeToHasSecureField, AtomicBoolean needToDeleteCache) { + Map typeToHasSecureField, AtomicBoolean needToDeleteCache, + Map typeParamIdentifierToParameterizedType) { return currentClassInfo .fields() .stream() .filter(fieldInfo -> !fieldInfo.hasAnnotation(JSON_IGNORE)) .map(FieldInfo::type) + .map(fieldType -> { + if (typeParamIdentifierToParameterizedType != null && fieldType.kind() == Type.Kind.TYPE_VARIABLE) { + var typeVariable = typeParamIdentifierToParameterizedType.get(fieldType.asTypeVariable().identifier()); + if (typeVariable != null) { + return typeVariable; + } + } + return fieldType; + }) .anyMatch(fieldType -> fieldTypeHasSecureFields(fieldType, indexView, typeToHasSecureField, needToDeleteCache)); } @@ -629,7 +661,7 @@ private static boolean fieldTypeHasSecureFields(Type fieldType, IndexView indexV return false; } final ClassInfo fieldClass = indexView.getClassByName(fieldType.name()); - return fieldClass != null && hasSecureFields(indexView, fieldClass, typeToHasSecureField, needToDeleteCache); + return fieldClass != null && hasSecureFields(indexView, fieldClass, typeToHasSecureField, needToDeleteCache, null); } if (fieldType.kind() == Type.Kind.ARRAY) { return fieldTypeHasSecureFields(fieldType.asArrayType().constituent(), indexView, typeToHasSecureField, diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/DisableSecureSerializationTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/DisableSecureSerializationTest.java new file mode 100644 index 0000000000000..e033c46cd2b1b --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/DisableSecureSerializationTest.java @@ -0,0 +1,94 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.resteasy.reactive.jackson.DisableSecureSerialization; +import io.quarkus.resteasy.reactive.jackson.EnableSecureSerialization; +import io.quarkus.resteasy.reactive.jackson.SecureField; +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.response.ValidatableResponse; + +public class DisableSecureSerializationTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(TestIdentityProvider.class, TestIdentityController.class)); + + @Test + public void testDisablingOfSecureSerialization() { + request("disabled", "user").body("secretField", Matchers.is("secret")); + request("disabled", "admin").body("secretField", Matchers.is("secret")); + request("enabled", "user").body("secretField", Matchers.nullValue()); + request("enabled", "admin").body("secretField", Matchers.is("secret")); + } + + private static ValidatableResponse request(String subPath, String user) { + TestIdentityController.resetRoles().add(user, user, user); + return RestAssured + .with() + .auth().preemptive().basic(user, user) + .get("/test/" + subPath) + .then() + .statusCode(200) + .body("publicField", Matchers.is("public")); + } + + @DisableSecureSerialization + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("test") + public static class GreetingsResource { + + @Path("disabled") + @GET + public Dto disabled() { + return Dto.createDto(); + } + + @EnableSecureSerialization + @Path("enabled") + @GET + public Dto enabled() { + return Dto.createDto(); + } + } + + public static class Dto { + + public Dto(String secretField, String publicField) { + this.secretField = secretField; + this.publicField = publicField; + } + + @SecureField(rolesAllowed = "admin") + private final String secretField; + + private final String publicField; + + public String getSecretField() { + return secretField; + } + + public String getPublicField() { + return publicField; + } + + private static Dto createDto() { + return new Dto("secret", "public"); + } + } +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Fruit.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Fruit.java new file mode 100644 index 0000000000000..32b9d539de12c --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Fruit.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import java.util.List; + +public class Fruit { + + public String name; + + public List prices; + + public Fruit(String name, Float price) { + this.name = name; + this.prices = List.of(new Price("USD", price)); + } + +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/GenericWrapper.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/GenericWrapper.java new file mode 100644 index 0000000000000..c661128e6220c --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/GenericWrapper.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +public class GenericWrapper { + + public String name; + + public T entity; + + public GenericWrapper(String name, T entity) { + this.name = name; + this.entity = entity; + } + +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Price.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Price.java new file mode 100644 index 0000000000000..f457ef415fd21 --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Price.java @@ -0,0 +1,17 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import io.quarkus.resteasy.reactive.jackson.SecureField; + +public class Price { + + @SecureField(rolesAllowed = "admin") + public Float price; + + public String currency; + + public Price(String currency, Float price) { + this.currency = currency; + this.price = price; + } + +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java index 861f01ce08a96..2b79bbadada79 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java @@ -33,7 +33,6 @@ import io.quarkus.resteasy.reactive.jackson.CustomDeserialization; import io.quarkus.resteasy.reactive.jackson.CustomSerialization; import io.quarkus.resteasy.reactive.jackson.DisableSecureSerialization; -import io.quarkus.resteasy.reactive.jackson.EnableSecureSerialization; import io.quarkus.runtime.BlockingOperationControl; import io.smallrye.common.annotation.NonBlocking; import io.smallrye.mutiny.Multi; @@ -41,7 +40,6 @@ @Path("/simple") @NonBlocking -@DisableSecureSerialization public class SimpleJsonResource extends SuperClass { @ServerExceptionMapper @@ -50,6 +48,7 @@ public Response handleParseException(WebApplicationException e) { return Response.status(Response.Status.BAD_REQUEST).entity(cause.getMessage()).build(); } + @DisableSecureSerialization @GET @Path("/person") public Person getPerson() { @@ -61,7 +60,6 @@ public Person getPerson() { return person; } - @EnableSecureSerialization @GET @Path("/frog") public Frog getFrog() { @@ -74,35 +72,30 @@ public Frog getFrog() { return frog; } - @EnableSecureSerialization @GET @Path("/frog-body-parts") public FrogBodyParts getFrogBodyParts() { return new FrogBodyParts("protruding eyes"); } - @EnableSecureSerialization @GET @Path("/interface-dog") public SecuredPersonInterface getInterfaceDog() { return createDog(); } - @EnableSecureSerialization @GET @Path("/abstract-dog") public AbstractPet getAbstractDog() { return createDog(); } - @EnableSecureSerialization @GET @Path("/abstract-named-dog") public AbstractNamedPet getAbstractNamedDog() { return createDog(); } - @EnableSecureSerialization @GET @Path("/dog") public Dog getDog() { @@ -130,48 +123,48 @@ public MapWrapper echoNullMap(MapWrapper mapWrapper) { return mapWrapper; } - @EnableSecureSerialization @GET @Path("/abstract-cat") public AbstractPet getAbstractCat() { return createCat(); } - @EnableSecureSerialization @GET @Path("/interface-cat") public SecuredPersonInterface getInterfaceCat() { return createCat(); } - @EnableSecureSerialization @GET @Path("/abstract-named-cat") public AbstractNamedPet getAbstractNamedCat() { return createCat(); } - @EnableSecureSerialization @GET @Path("/cat") public Cat getCat() { return createCat(); } - @EnableSecureSerialization @GET @Path("/unsecured-pet") public UnsecuredPet getUnsecuredPet() { return createUnsecuredPet(); } - @EnableSecureSerialization @GET @Path("/abstract-unsecured-pet") public AbstractUnsecuredPet getAbstractUnsecuredPet() { return createUnsecuredPet(); } + @GET + @Path("/secure-field-on-type-variable") + public GenericWrapper getWithSecureFieldOnTypeVariable() { + return new GenericWrapper<>("wrapper", new Fruit("Apple", 1.0f)); + } + private static UnsecuredPet createUnsecuredPet() { var pet = new UnsecuredPet(); pet.setPublicName("Unknown"); @@ -212,6 +205,7 @@ public Person getCustomSerializedPerson() { return getPerson(); } + @DisableSecureSerialization @CustomDeserialization(UnquotedFieldsPersonDeserialization.class) @POST @Path("custom-deserialized-person") @@ -219,7 +213,6 @@ public Person echoCustomDeserializedPerson(Person request) { return request; } - @EnableSecureSerialization @GET @Path("secure-person") public Person getSecurePerson() { @@ -227,7 +220,6 @@ public Person getSecurePerson() { } @JsonView(Views.Public.class) - @EnableSecureSerialization @GET @Path("secure-person-with-public-view") public Person getSecurePersonWithPublicView() { @@ -235,7 +227,6 @@ public Person getSecurePersonWithPublicView() { } @JsonView(Views.Public.class) - @EnableSecureSerialization @GET @Path("uni-secure-person-with-public-view") public Uni getUniSecurePersonWithPublicView() { @@ -243,41 +234,37 @@ public Uni getUniSecurePersonWithPublicView() { } @JsonView(Views.Private.class) - @EnableSecureSerialization @GET @Path("secure-person-with-private-view") public Person getSecurePersonWithPrivateView() { return getPerson(); } - @EnableSecureSerialization @GET @Path("secure-uni-person") public Uni getSecureUniPerson() { return Uni.createFrom().item(getPerson()); } - @EnableSecureSerialization @GET @Path("secure-rest-response-person") public RestResponse getSecureRestResponsePerson() { return RestResponse.ok(getPerson()); } - @EnableSecureSerialization @GET @Path("secure-people") public List getSecurePeople() { return Collections.singletonList(getPerson()); } - @EnableSecureSerialization @GET @Path("secure-uni-people") public Uni> getSecureUniPeople() { return Uni.createFrom().item(Collections.singletonList(getPerson())); } + @DisableSecureSerialization @POST @Path("/person") @Produces(MediaType.APPLICATION_JSON) @@ -322,6 +309,7 @@ public Response getPersonCustomMediaTypeResponseWithType(Person person) { return Response.ok(person).status(201).header("Content-Type", "application/vnd.quarkus.other-v1+json").build(); } + @DisableSecureSerialization @POST @Path("/people") @Consumes(MediaType.APPLICATION_JSON) @@ -354,6 +342,7 @@ public List strings(List strings) { return strings; } + @DisableSecureSerialization @POST @Path("/person-large") @Produces(MediaType.APPLICATION_JSON) @@ -365,6 +354,7 @@ public Person personTest(Person person) { return person; } + @DisableSecureSerialization @POST @Path("/person-validated") @Produces(MediaType.APPLICATION_JSON) diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java index a5fa4d498c923..4d1092f546893 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java @@ -36,7 +36,8 @@ public JavaArchive get() { AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class, AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class, Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class, - NestedInterface.class, StateRecord.class, MapWrapper.class) + NestedInterface.class, StateRecord.class, MapWrapper.class, GenericWrapper.class, + Fruit.class, Price.class) .addAsResource(new StringAsset("admin-expression=admin\n" + "user-expression=user\n" + "birth-date-roles=alice,bob\n"), "application.properties"); @@ -584,6 +585,26 @@ public void testSecureFieldOnArrayTypeField() { .body("parts[0].name", Matchers.is("protruding eyes")); } + @Test + public void testSecureFieldOnTypeVariable() { + TestIdentityController.resetRoles().add("max", "max", "user"); + RestAssured + .with() + .auth().preemptive().basic("max", "max") + .get("/simple/secure-field-on-type-variable") + .then() + .statusCode(200) + .body("entity.prices[0].price", Matchers.nullValue()); + TestIdentityController.resetRoles().add("rolfe", "rolfe", "admin"); + RestAssured + .with() + .auth().preemptive().basic("rolfe", "rolfe") + .get("/simple/secure-field-on-type-variable") + .then() + .statusCode(200) + .body("entity.prices[0].price", Matchers.notNullValue()); + } + private static void testSecuredFieldOnReturnTypeField(String subPath) { TestIdentityController.resetRoles().add("max", "max", "user"); RestAssured diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java index 10ea3d373ce91..d0fdd70214f1b 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java @@ -5,6 +5,8 @@ import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.security.test.utils.TestIdentityController; @@ -25,7 +27,8 @@ public JavaArchive get() { AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class, AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class, Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class, - NestedInterface.class, StateRecord.class, MapWrapper.class) + NestedInterface.class, StateRecord.class, MapWrapper.class, GenericWrapper.class, + Fruit.class, Price.class) .addAsResource(new StringAsset("admin-expression=admin\n" + "user-expression=user\n" + "birth-date-roles=alice,bob\n" + @@ -33,4 +36,11 @@ public JavaArchive get() { "application.properties"); } }); + + @Disabled("Doesn't work with the reflection free serializers") + @Test + @Override + public void testSecureFieldOnTypeVariable() { + super.testSecureFieldOnTypeVariable(); + } }