diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 13577f5aff47b..c8110375d57ed 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -72,6 +72,7 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.arc.runtime.ClientProxyUnwrapper; import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; import io.quarkus.deployment.GeneratedClassGizmoAdaptor; import io.quarkus.deployment.annotations.BuildProducer; @@ -80,6 +81,7 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; @@ -108,6 +110,7 @@ import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationRedirectExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.ForbiddenExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.UnauthorizedExceptionMapper; +import io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityHandler; import io.quarkus.resteasy.reactive.server.runtime.security.SecurityContextOverrideHandler; import io.quarkus.resteasy.reactive.server.spi.MethodScannerBuildItem; import io.quarkus.resteasy.reactive.spi.CustomExceptionMapperBuildItem; @@ -544,6 +547,8 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an .setDynamicFeatures(dynamicFeats) .setSerialisers(serialisers) .setApplicationPath(applicationPath) + .setGlobalHandlerCustomers( + new ArrayList<>(Collections.singletonList(new SecurityContextOverrideHandler.Customizer()))) //TODO: should be pluggable .setResourceClasses(resourceClasses) .setLocatableResourceClasses(subResourceClasses) .setParamConverterProviders(paramConverterProviders); @@ -632,12 +637,26 @@ public void securityExceptionMappers(BuildProducer exc } @BuildStep - MethodScannerBuildItem integrateSecurityOverrideSupport() { + MethodScannerBuildItem integrateEagerSecurity(Capabilities capabilities, CombinedIndexBuildItem indexBuildItem) { + if (!capabilities.isPresent(Capability.SECURITY)) { + return null; + } + var index = indexBuildItem.getComputingIndex(); return new MethodScannerBuildItem(new MethodScanner() { @Override public List scan(MethodInfo method, ClassInfo actualEndpointClass, Map methodContext) { - return Collections.singletonList(new SecurityContextOverrideHandler.Customizer()); + if (SecurityTransformerUtils.hasStandardSecurityAnnotation(method)) { + return Collections.singletonList(new EagerSecurityHandler.Customizer()); + } + ClassInfo c = actualEndpointClass; + while (c.superName() != null) { + if (SecurityTransformerUtils.hasStandardSecurityAnnotation(c)) { + return Collections.singletonList(new EagerSecurityHandler.Customizer()); + } + c = index.getClassByName(c.superName()); + } + return Collections.emptyList(); } }); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/SecurityTransformerUtils.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/SecurityTransformerUtils.java new file mode 100644 index 0000000000000..9a9ef2d9c0602 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/SecurityTransformerUtils.java @@ -0,0 +1,64 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; + +import io.quarkus.security.Authenticated; + +public class SecurityTransformerUtils { + public static final Set SECURITY_BINDINGS = new HashSet<>(); + + static { + // keep the contents the same as in io.quarkus.resteasy.deployment.SecurityTransformerUtils + SECURITY_BINDINGS.add(DotName.createSimple(RolesAllowed.class.getName())); + SECURITY_BINDINGS.add(DotName.createSimple(Authenticated.class.getName())); + SECURITY_BINDINGS.add(DotName.createSimple(DenyAll.class.getName())); + SECURITY_BINDINGS.add(DotName.createSimple(PermitAll.class.getName())); + } + + public static boolean hasStandardSecurityAnnotation(MethodInfo methodInfo) { + return hasStandardSecurityAnnotation(methodInfo.annotations()); + } + + public static boolean hasStandardSecurityAnnotation(ClassInfo classInfo) { + return hasStandardSecurityAnnotation(classInfo.classAnnotations()); + } + + private static boolean hasStandardSecurityAnnotation(Collection instances) { + for (AnnotationInstance instance : instances) { + if (SECURITY_BINDINGS.contains(instance.name())) { + return true; + } + } + return false; + } + + public static Optional findFirstStandardSecurityAnnotation(MethodInfo methodInfo) { + return findFirstStandardSecurityAnnotation(methodInfo.annotations()); + } + + public static Optional findFirstStandardSecurityAnnotation(ClassInfo classInfo) { + return findFirstStandardSecurityAnnotation(classInfo.classAnnotations()); + } + + private static Optional findFirstStandardSecurityAnnotation(Collection instances) { + for (AnnotationInstance instance : instances) { + if (SECURITY_BINDINGS.contains(instance.name())) { + return Optional.of(instance); + } + } + return Optional.empty(); + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/LazyAuthRolesAllowedJaxRsTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/LazyAuthRolesAllowedJaxRsTestCase.java index 8c6479555944b..05d04683d6858 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/LazyAuthRolesAllowedJaxRsTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/LazyAuthRolesAllowedJaxRsTestCase.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import java.util.Arrays; + import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -18,7 +20,7 @@ public class LazyAuthRolesAllowedJaxRsTestCase { @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(RolesAllowedResource.class, UserResource.class, + .addClasses(RolesAllowedResource.class, RolesAllowedBlockingResource.class, UserResource.class, TestIdentityProvider.class, TestIdentityController.class, UnsecuredSubResource.class) @@ -34,12 +36,14 @@ public static void setupUsers() { @Test public void testRolesAllowed() { - RestAssured.get("/roles").then().statusCode(401); - RestAssured.given().auth().basic("admin", "admin").get("/roles").then().statusCode(200); - RestAssured.given().auth().basic("admin", "wrong").get("/roles").then().statusCode(401); - RestAssured.given().auth().basic("user", "user").get("/roles").then().statusCode(200); - RestAssured.given().auth().basic("admin", "admin").get("/roles/admin").then().statusCode(200); - RestAssured.given().auth().basic("user", "user").get("/roles/admin").then().statusCode(403); + Arrays.asList("/roles", "/roles-blocking").forEach((path) -> { + RestAssured.get(path).then().statusCode(401); + RestAssured.given().auth().basic("admin", "admin").get(path).then().statusCode(200); + RestAssured.given().auth().basic("admin", "wrong").get(path).then().statusCode(401); + RestAssured.given().auth().basic("user", "user").get(path).then().statusCode(200); + RestAssured.given().auth().basic("admin", "admin").get(path + "/admin").then().statusCode(200); + RestAssured.given().auth().basic("user", "user").get(path + "/admin").then().statusCode(403); + }); } @Test diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java index 38492b3ef2346..8654e008e8bd8 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase.java @@ -2,6 +2,8 @@ import static org.hamcrest.Matchers.is; +import java.util.Arrays; + import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -18,7 +20,7 @@ public class ReplaceIdentityLazyAuthRolesAllowedJaxRsTestCase { @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(RolesAllowedResource.class, UserResource.class, + .addClasses(RolesAllowedResource.class, UserResource.class, RolesAllowedBlockingResource.class, TestIdentityProvider.class, TestIdentityController.class, SecurityOverrideFilter.class, @@ -36,14 +38,17 @@ public static void setupUsers() { @Test public void testRolesAllowedModified() { //make sure that things work as normal when no modification happens - RestAssured.given() - .header("user", "admin") - .header("role", "admin") - .get("/roles").then().statusCode(200); - RestAssured.given() - .auth().basic("user", "user") - .header("user", "admin") - .header("role", "admin").get("/roles/admin").then().statusCode(200); + + Arrays.asList("/roles", "/roles-blocking").forEach((path) -> { + RestAssured.given() + .header("user", "admin") + .header("role", "admin") + .get(path).then().statusCode(200); + RestAssured.given() + .auth().basic("user", "user") + .header("user", "admin") + .header("role", "admin").get(path + "/admin").then().statusCode(200); + }); } @Test diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedBlockingResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedBlockingResource.java new file mode 100644 index 0000000000000..6d44a2d7e0faf --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedBlockingResource.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import io.smallrye.common.annotation.Blocking; + +/** + * @author Michal Szynkiewicz, michal.l.szynkiewicz@gmail.com + */ +@Path("/roles-blocking") +@PermitAll +@Blocking +public class RolesAllowedBlockingResource { + @GET + @RolesAllowed({ "user", "admin" }) + public String defaultSecurity() { + return "default"; + } + + @Path("/admin") + @RolesAllowed("admin") + @GET + public String admin() { + return "admin"; + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedJaxRsTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedJaxRsTestCase.java index e55d06541c6c6..9f46e69e9d9e1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedJaxRsTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedJaxRsTestCase.java @@ -2,8 +2,21 @@ import static org.hamcrest.Matchers.is; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Arrays; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; + import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -17,7 +30,8 @@ public class RolesAllowedJaxRsTestCase { @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(RolesAllowedResource.class, UserResource.class, + .addClasses(RolesAllowedResource.class, UserResource.class, RolesAllowedBlockingResource.class, + SerializationEntity.class, SerializationRolesResource.class, TestIdentityProvider.class, TestIdentityController.class, UnsecuredSubResource.class)); @@ -31,11 +45,13 @@ public static void setupUsers() { @Test public void testRolesAllowed() { - RestAssured.get("/roles").then().statusCode(401); - RestAssured.given().auth().basic("admin", "admin").get("/roles").then().statusCode(200); - RestAssured.given().auth().basic("user", "user").get("/roles").then().statusCode(200); - RestAssured.given().auth().basic("admin", "admin").get("/roles/admin").then().statusCode(200); - RestAssured.given().auth().basic("user", "user").get("/roles/admin").then().statusCode(403); + Arrays.asList("/roles", "/roles-blocking").forEach((path) -> { + RestAssured.get(path).then().statusCode(401); + RestAssured.given().auth().basic("admin", "admin").get(path).then().statusCode(200); + RestAssured.given().auth().basic("user", "user").get(path).then().statusCode(200); + RestAssured.given().auth().basic("admin", "admin").get(path + "/admin").then().statusCode(200); + RestAssured.given().auth().basic("user", "user").get(path + "/admin").then().statusCode(403); + }); } @Test @@ -46,4 +62,47 @@ public void testUser() { RestAssured.given().auth().basic("user", "user").get("/user").then().body(is("")); RestAssured.given().auth().preemptive().basic("user", "user").get("/user").then().body(is("user")); } + + @Test + public void testSecurityRunsBeforeValidation() { + read = false; + RestAssured.given().body(new SerializationEntity()).post("/roles-validate").then().statusCode(401); + Assertions.assertFalse(read); + RestAssured.given().body(new SerializationEntity()).auth().basic("admin", "admin").post("/roles-validate").then() + .statusCode(200); + Assertions.assertTrue(read); + read = false; + RestAssured.given().body(new SerializationEntity()).auth().basic("user", "user").post("/roles-validate").then() + .statusCode(200); + Assertions.assertTrue(read); + read = false; + RestAssured.given().body(new SerializationEntity()).auth().basic("admin", "admin").post("/roles-validate/admin").then() + .statusCode(200); + Assertions.assertTrue(read); + read = false; + RestAssured.given().body(new SerializationEntity()).auth().basic("user", "user").post("/roles-validate/admin").then() + .statusCode(403); + Assertions.assertFalse(read); + } + + static volatile boolean read = false; + + @Provider + public static class Reader implements MessageBodyReader { + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return true; + } + + @Override + public SerializationEntity readFrom(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + read = true; + SerializationEntity entity = new SerializationEntity(); + entity.setName("read"); + return entity; + } + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java index 51279ee6b74a1..842e04d7d0ec8 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java @@ -5,14 +5,11 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; -import io.smallrye.common.annotation.Blocking; - /** * @author Michal Szynkiewicz, michal.l.szynkiewicz@gmail.com */ @Path("/roles") @PermitAll -@Blocking public class RolesAllowedResource { @GET @RolesAllowed({ "user", "admin" }) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationEntity.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationEntity.java new file mode 100644 index 0000000000000..bd1a9774f5262 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationEntity.java @@ -0,0 +1,14 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +public class SerializationEntity { + private String name; + + public String getName() { + return name; + } + + public SerializationEntity setName(String name) { + this.name = name; + return this; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationRolesResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationRolesResource.java new file mode 100644 index 0000000000000..e2b273183fa33 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SerializationRolesResource.java @@ -0,0 +1,27 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import io.smallrye.common.annotation.Blocking; + +@Path("/roles-validate") +@PermitAll +@Blocking +public class SerializationRolesResource { + @POST + @RolesAllowed({ "user", "admin" }) + public String defaultSecurity(SerializationEntity entity) { + return entity.getName(); + } + + @Path("/admin") + @RolesAllowed("admin") + @POST + public String admin(SerializationEntity entity) { + return entity.getName(); + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java index 33ae9c3b599cf..7316ccc0dbb70 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java @@ -18,6 +18,7 @@ public class QuarkusResteasyReactiveRequestContext extends VertxResteasyReactiveRequestContext { final CurrentIdentityAssociation association; + boolean userSetup = false; public QuarkusResteasyReactiveRequestContext(Deployment deployment, ProvidersImpl providers, RoutingContext context, ThreadSetupAction requestContext, ServerRestHandler[] handlerChain, @@ -29,7 +30,8 @@ public QuarkusResteasyReactiveRequestContext(Deployment deployment, ProvidersImp protected void handleRequestScopeActivation() { super.handleRequestScopeActivation(); - if (association != null) { + if (!userSetup && association != null) { + userSetup = true; QuarkusHttpUser existing = (QuarkusHttpUser) context.user(); if (existing != null) { SecurityIdentity identity = existing.getSecurityIdentity(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java new file mode 100644 index 0000000000000..47ce0b0afa449 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java @@ -0,0 +1,97 @@ +package io.quarkus.resteasy.reactive.server.runtime.security; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import org.jboss.resteasy.reactive.common.model.ResourceClass; +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer; +import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InjectableInstance; +import io.quarkus.security.identity.CurrentIdentityAssociation; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; +import io.smallrye.mutiny.subscription.UniSubscriber; +import io.smallrye.mutiny.subscription.UniSubscription; + +public class EagerSecurityHandler implements ServerRestHandler { + + private static final SecurityCheck NULL_SENTINEL = new SecurityCheck() { + @Override + public void apply(SecurityIdentity identity, Method method, Object[] parameters) { + + } + }; + + private volatile InjectableInstance currentIdentityAssociation; + private volatile SecurityCheck check; + + @Override + public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { + SecurityCheck check = this.check; + if (check == null) { + check = Arc.container().instance(SecurityCheckStorage.class).get() + .getSecurityCheck(requestContext.getTarget().getLazyMethod().getMethod()); + if (check == null) { + check = NULL_SENTINEL; + } + this.check = check; + } + if (check == NULL_SENTINEL) { + return; + } + requestContext.requireCDIRequestScope(); + requestContext.suspend(); + SecurityCheck theCheck = check; + getCurrentIdentityAssociation().get().getDeferredIdentity().map(new Function() { + @Override + public Object apply(SecurityIdentity securityIdentity) { + theCheck.apply(securityIdentity, requestContext.getTarget().getLazyMethod().getMethod(), + requestContext.getParameters()); + return null; + } + }) + .subscribe().withSubscriber(new UniSubscriber() { + @Override + public void onSubscribe(UniSubscription subscription) { + + } + + @Override + public void onItem(Object item) { + requestContext.resume(); + } + + @Override + public void onFailure(Throwable failure) { + requestContext.resume(failure); + } + }); + + } + + private InjectableInstance getCurrentIdentityAssociation() { + InjectableInstance identityAssociation = this.currentIdentityAssociation; + if (identityAssociation == null) { + return this.currentIdentityAssociation = Arc.container().select(CurrentIdentityAssociation.class); + } + return identityAssociation; + } + + public static class Customizer implements HandlerChainCustomizer { + @Override + public List handlers(Phase phase, ResourceClass resourceClass, + ServerResourceMethod serverResourceMethod) { + if (phase == Phase.AFTER_MATCH) { + return Collections.singletonList(new EagerSecurityHandler()); + } + return Collections.emptyList(); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java index 640b9ae838a67..f118d009c7875 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java @@ -122,7 +122,10 @@ public static class Customizer implements HandlerChainCustomizer { @Override public List handlers(Phase phase, ResourceClass resourceClass, ServerResourceMethod serverResourceMethod) { - return Collections.singletonList(new SecurityContextOverrideHandler()); + if (phase == Phase.AFTER_PRE_MATCH) { + return Collections.singletonList(new SecurityContextOverrideHandler()); + } + return Collections.emptyList(); } } } diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/AdditionalSecurityCheckBuildItem.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/AdditionalSecurityCheckBuildItem.java index e375e13b05762..6a026748a7ddb 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/AdditionalSecurityCheckBuildItem.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/AdditionalSecurityCheckBuildItem.java @@ -3,7 +3,7 @@ import org.jboss.jandex.MethodInfo; import io.quarkus.builder.item.MultiBuildItem; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; /** * Used as an integration point when extensions need to customize the security behavior of a bean diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java index d6bd449991c80..38bbfd7767438 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java @@ -17,6 +17,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import javax.enterprise.context.ApplicationScoped; @@ -59,13 +60,13 @@ import io.quarkus.security.runtime.interceptor.DenyAllInterceptor; import io.quarkus.security.runtime.interceptor.PermitAllInterceptor; import io.quarkus.security.runtime.interceptor.RolesAllowedInterceptor; -import io.quarkus.security.runtime.interceptor.SecurityCheckStorage; import io.quarkus.security.runtime.interceptor.SecurityCheckStorageBuilder; import io.quarkus.security.runtime.interceptor.SecurityConstrainer; import io.quarkus.security.runtime.interceptor.SecurityHandler; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; import io.quarkus.security.spi.AdditionalSecuredClassesBuildItem; import io.quarkus.security.spi.runtime.AuthorizationController; +import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; public class SecurityProcessor { @@ -321,7 +322,7 @@ void gatherSecurityChecks(BuildProducer syntheticBeans, List additionalSecuredClasses, SecurityCheckRecorder recorder, List additionalSecurityChecks, SecurityBuildTimeConfig config) { - classPredicate.produce(new ApplicationClassPredicateBuildItem(new SecurityCheckStorage.AppPredicate())); + classPredicate.produce(new ApplicationClassPredicateBuildItem(new SecurityCheckStorageAppPredicate())); final Map additionalSecured = new HashMap<>(); for (AdditionalSecuredClassesBuildItem securedClasses : additionalSecuredClasses) { @@ -510,4 +511,12 @@ static class AdditionalSecured { this.rolesAllowed = rolesAllowed; } } + + class SecurityCheckStorageAppPredicate implements Predicate { + + @Override + public boolean test(String s) { + return s.equals(SecurityCheckStorage.class.getName()); + } + } } diff --git a/extensions/security/runtime-spi/pom.xml b/extensions/security/runtime-spi/pom.xml index cffacf35a78d2..d1136b1e0c229 100644 --- a/extensions/security/runtime-spi/pom.xml +++ b/extensions/security/runtime-spi/pom.xml @@ -17,6 +17,10 @@ io.quarkus quarkus-core + + io.quarkus.security + quarkus-security + diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SecurityCheck.java b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheck.java similarity index 78% rename from extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SecurityCheck.java rename to extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheck.java index bee1e06417c61..ba99d500f77f0 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SecurityCheck.java +++ b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheck.java @@ -1,4 +1,4 @@ -package io.quarkus.security.runtime.interceptor.check; +package io.quarkus.security.spi.runtime; import java.lang.reflect.Method; diff --git a/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheckStorage.java b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheckStorage.java new file mode 100644 index 0000000000000..100473577027b --- /dev/null +++ b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityCheckStorage.java @@ -0,0 +1,8 @@ +package io.quarkus.security.spi.runtime; + +import java.lang.reflect.Method; + +public interface SecurityCheckStorage { + SecurityCheck getSecurityCheck(Method method); + +} diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityCheckRecorder.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityCheckRecorder.java index a467e2bf93e8e..4d3b4e0d23e77 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityCheckRecorder.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityCheckRecorder.java @@ -2,13 +2,13 @@ import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.security.runtime.interceptor.SecurityCheckStorage; import io.quarkus.security.runtime.interceptor.SecurityCheckStorageBuilder; import io.quarkus.security.runtime.interceptor.check.AuthenticatedCheck; import io.quarkus.security.runtime.interceptor.check.DenyAllCheck; import io.quarkus.security.runtime.interceptor.check.PermitAllCheck; import io.quarkus.security.runtime.interceptor.check.RolesAllowedCheck; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; @Recorder public class SecurityCheckRecorder { diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorage.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorage.java deleted file mode 100644 index 5433c30d842fe..0000000000000 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorage.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.quarkus.security.runtime.interceptor; - -import java.lang.reflect.Method; -import java.util.function.Predicate; - -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; - -public interface SecurityCheckStorage { - SecurityCheck getSecurityCheck(Method method); - - class AppPredicate implements Predicate { - - @Override - public boolean test(String s) { - return s.equals(SecurityCheckStorage.class.getName()); - } - } -} diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorageBuilder.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorageBuilder.java index 667614a32d180..dda9083bfae9a 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorageBuilder.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityCheckStorageBuilder.java @@ -6,7 +6,8 @@ import java.util.Map; import java.util.Objects; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; public class SecurityCheckStorageBuilder { private final Map securityChecks = new HashMap<>(); diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java index 380d804b55500..75ba078df3060 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java @@ -6,7 +6,8 @@ import javax.inject.Singleton; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; /** * @author Michal Szynkiewicz, michal.l.szynkiewicz@gmail.com diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/AuthenticatedCheck.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/AuthenticatedCheck.java index 7f05682ac1323..4b23692988914 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/AuthenticatedCheck.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/AuthenticatedCheck.java @@ -4,6 +4,7 @@ import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; public class AuthenticatedCheck implements SecurityCheck { diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/DenyAllCheck.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/DenyAllCheck.java index 70e1d0d832771..af0c0f5a97fd4 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/DenyAllCheck.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/DenyAllCheck.java @@ -5,6 +5,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; public class DenyAllCheck implements SecurityCheck { diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/PermitAllCheck.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/PermitAllCheck.java index c3be76e05f1d4..ef8e023db721c 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/PermitAllCheck.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/PermitAllCheck.java @@ -3,6 +3,7 @@ import java.lang.reflect.Method; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; public class PermitAllCheck implements SecurityCheck { diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/RolesAllowedCheck.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/RolesAllowedCheck.java index 51f072727411c..3ea82eb109ef0 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/RolesAllowedCheck.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/RolesAllowedCheck.java @@ -12,6 +12,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; public class RolesAllowedCheck implements SecurityCheck { diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SupplierRolesAllowedCheck.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SupplierRolesAllowedCheck.java index 156c9d983ca43..f4b3cb2ce9f53 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SupplierRolesAllowedCheck.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/check/SupplierRolesAllowedCheck.java @@ -6,6 +6,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.SecurityCheck; public class SupplierRolesAllowedCheck implements SecurityCheck { diff --git a/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/SpringSecurityProcessor.java b/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/SpringSecurityProcessor.java index d6c7a8cd702e9..5b0e2850b53c9 100644 --- a/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/SpringSecurityProcessor.java +++ b/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/SpringSecurityProcessor.java @@ -43,7 +43,7 @@ import io.quarkus.security.deployment.AdditionalSecurityCheckBuildItem; import io.quarkus.security.deployment.SecurityTransformerUtils; import io.quarkus.security.runtime.SecurityCheckRecorder; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; import io.quarkus.spring.di.deployment.SpringBeanNameToDotNameBuildItem; import io.quarkus.spring.security.runtime.interceptor.SpringPreauthorizeInterceptor; import io.quarkus.spring.security.runtime.interceptor.SpringSecuredInterceptor; diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java index 21088706d7f61..8101bdd0991b6 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java @@ -5,8 +5,8 @@ import java.util.function.Supplier; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; import io.quarkus.security.runtime.interceptor.check.SupplierRolesAllowedCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; import io.quarkus.spring.security.runtime.interceptor.check.AllDelegatingSecurityCheck; import io.quarkus.spring.security.runtime.interceptor.check.AnonymousCheck; import io.quarkus.spring.security.runtime.interceptor.check.AnyDelegatingSecurityCheck; diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AbstractBeanMethodSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AbstractBeanMethodSecurityCheck.java index 0cab79d07c943..429d30a4d0aed 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AbstractBeanMethodSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AbstractBeanMethodSecurityCheck.java @@ -5,7 +5,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; /** * Implementations of this class are generated for expressions in @PreAuthorize that diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AllDelegatingSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AllDelegatingSecurityCheck.java index f3143449ca46e..0e728121457bd 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AllDelegatingSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AllDelegatingSecurityCheck.java @@ -4,7 +4,7 @@ import java.util.List; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; /** * A {@link SecurityCheck} where all delegates must pass diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnonymousCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnonymousCheck.java index b6dced359c394..b538176065e9b 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnonymousCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnonymousCheck.java @@ -4,7 +4,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; public class AnonymousCheck implements SecurityCheck { diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnyDelegatingSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnyDelegatingSecurityCheck.java index 7019905db9f31..fdb35a5299f4c 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnyDelegatingSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/AnyDelegatingSecurityCheck.java @@ -4,7 +4,7 @@ import java.util.List; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; /** * A {@link SecurityCheck} where if any of the delegates passes the security check then diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java index 21ddf73a279a5..8ebee37977075 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java @@ -6,7 +6,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; import io.quarkus.spring.security.runtime.interceptor.accessor.StringPropertyAccessor; /** diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterSecurityCheck.java index e144cdf3759cc..3f2f1f7116daf 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterSecurityCheck.java @@ -5,7 +5,7 @@ import io.quarkus.security.ForbiddenException; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.interceptor.check.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheck; /** * Instances of this classes are created in order to check if a method parameter diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java index 2d77b754d7f8f..cba84c1d8852a 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/DeploymentInfo.java @@ -1,6 +1,6 @@ package org.jboss.resteasy.reactive.server.core; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; @@ -31,7 +31,7 @@ public class DeploymentInfo { private ResteasyReactiveConfig config; private Function clientProxyUnwrapper; private String applicationPath; - private List globalHandlerCustomers = Collections.emptyList(); + private List globalHandlerCustomers = new ArrayList<>(); public ResourceInterceptors getInterceptors() { return interceptors; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java index c104fdc54bab8..21214b9fec9ae 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java @@ -198,7 +198,8 @@ public BeanFactory.BeanInstance apply(Class aClass) { } for (int i = 0; i < info.getGlobalHandlerCustomizers().size(); i++) { preMatchHandlers - .addAll(info.getGlobalHandlerCustomizers().get(i).handlers(HandlerChainCustomizer.Phase.AFTER_PRE_MATCH)); + .addAll(info.getGlobalHandlerCustomizers().get(i).handlers(HandlerChainCustomizer.Phase.AFTER_PRE_MATCH, + null, null)); } return new Deployment(exceptionMapping, info.getCtxResolvers(), serialisers, diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java index a28a1e1b7cca7..961c93ac02295 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java @@ -127,8 +127,6 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz, MultivaluedMap score = new QuarkusMultivaluedHashMap<>(); Map pathParameterIndexes = buildParamIndexMap(classPathTemplate, methodPathTemplate); - List handlers = new ArrayList<>(); - addHandlers(handlers, clazz, method, info, HandlerChainCustomizer.Phase.AFTER_MATCH); MediaType sseElementType = null; if (method.getSseElementType() != null) { sseElementType = MediaType.valueOf(method.getSseElementType()); @@ -159,10 +157,13 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz, .forMethod(method, lazyMethod); //setup reader and writer interceptors first - handlers.addAll(interceptorDeployment.setupInterceptorHandler()); - //at this point the handler chain only has interceptors - //which we also want in the abort handler chain - List abortHandlingChain = new ArrayList<>(handlers); + List interceptorHandlers = interceptorDeployment.setupInterceptorHandler(); + //we want interceptors in the abort handler chain + List abortHandlingChain = new ArrayList<>(interceptorHandlers); + + List handlers = new ArrayList<>(); + addHandlers(handlers, clazz, method, info, HandlerChainCustomizer.Phase.AFTER_MATCH); + handlers.addAll(interceptorHandlers); // when a method is blocking, we also want all the request filters to run on the worker thread // because they can potentially set thread local variables diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java index 4178747cd4704..25934998c399c 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java @@ -308,7 +308,9 @@ public ServerHttpResponse setStatusCode(int code) { @Override public ServerHttpResponse end() { - response.end(); + if (!response.ended()) { + response.end(); + } return this; }