diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml
index 02095903a9cff..5138f26ac0fdd 100644
--- a/bom/runtime/pom.xml
+++ b/bom/runtime/pom.xml
@@ -24,7 +24,7 @@
0.2.0
0.0.12
0.34.0
- 3.0.0.Final
+ 3.0.1.Final
1.0.0.Final
1.3
1.0.1
diff --git a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java
index f9775d62682b0..468ddc56e4e7a 100644
--- a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java
+++ b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java
@@ -1,5 +1,7 @@
package io.quarkus.keycloak.pep.deployment;
+import java.util.Map;
+
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
@@ -13,6 +15,7 @@
import io.quarkus.oidc.OIDCException;
import io.quarkus.oidc.runtime.OidcBuildTimeConfig;
import io.quarkus.oidc.runtime.OidcConfig;
+import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem;
public class KeycloakPolicyEnforcerBuildStep {
@@ -21,6 +24,34 @@ FeatureBuildItem featureBuildItem() {
return new FeatureBuildItem(FeatureBuildItem.KEYCLOAK_AUTHORIZATION);
}
+ @BuildStep
+ RequireBodyHandlerBuildItem requireBody(KeycloakPolicyEnforcerConfig config) {
+ if (config.policyEnforcer.enable) {
+ if (isBodyClaimInformationPointDefined(config.policyEnforcer.claimInformationPoint.simpleConfig)) {
+ return new RequireBodyHandlerBuildItem();
+ }
+ for (KeycloakPolicyEnforcerConfig.KeycloakConfigPolicyEnforcer.PathConfig path : config.policyEnforcer.paths
+ .values()) {
+ if (isBodyClaimInformationPointDefined(path.claimInformationPoint.simpleConfig)) {
+ return new RequireBodyHandlerBuildItem();
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean isBodyClaimInformationPointDefined(Map> claims) {
+ for (Map.Entry> entry : claims.entrySet()) {
+ Map value = entry.getValue();
+
+ if (value.get(entry.getKey()).contains("request.body")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@BuildStep
public AdditionalBeanBuildItem beans(KeycloakPolicyEnforcerConfig config) {
if (config.policyEnforcer.enable) {
diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfig.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfig.java
index 9f6545124b69f..118ed1247c348 100644
--- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfig.java
+++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerConfig.java
@@ -41,13 +41,13 @@ public static class KeycloakConfigPolicyEnforcer {
* Specifies how policies are enforced.
*/
@ConfigItem(defaultValue = "ENFORCING")
- String enforcementMode;
+ public String enforcementMode;
/**
* Specifies the paths to protect.
*/
@ConfigItem
- Map paths;
+ public Map paths;
/**
* Defines how the policy enforcer should track associations between paths in your application and resources defined in
@@ -56,7 +56,7 @@ public static class KeycloakConfigPolicyEnforcer {
* protected resources
*/
@ConfigItem
- PathCacheConfig pathCache;
+ public PathCacheConfig pathCache;
/**
* Specifies how the adapter should fetch the server for resources associated with paths in your application. If true,
@@ -65,14 +65,14 @@ public static class KeycloakConfigPolicyEnforcer {
* enforcer is going to fetch resources on-demand accordingly with the path being requested
*/
@ConfigItem(defaultValue = "true")
- boolean lazyLoadPaths;
+ public boolean lazyLoadPaths;
/**
* Defines a set of one or more claims that must be resolved and pushed to the Keycloak server in order to make these
* claims available to policies
*/
@ConfigItem
- ClaimInformationPointConfig claimInformationPoint;
+ public ClaimInformationPointConfig claimInformationPoint;
/**
* Specifies how scopes should be mapped to HTTP methods. If set to true, the policy enforcer will use the HTTP method
@@ -80,7 +80,7 @@ public static class KeycloakConfigPolicyEnforcer {
* the current request to check whether or not access should be granted
*/
@ConfigItem
- boolean httpMethodAsScope;
+ public boolean httpMethodAsScope;
@ConfigGroup
public static class PathConfig {
@@ -89,13 +89,13 @@ public static class PathConfig {
* The name of a resource on the server that is to be associated with a given path
*/
@ConfigItem
- Optional name;
+ public Optional name;
/**
* A URI relative to the application’s context path that should be protected by the policy enforcer
*/
@ConfigItem
- Optional path;
+ public Optional path;
/**
* The HTTP methods (for example, GET, POST, PATCH) to protect and how they are associated with the scopes for a
@@ -103,14 +103,14 @@ public static class PathConfig {
* resource in the server
*/
@ConfigItem
- Map methods;
+ public Map methods;
/**
* Specifies how policies are enforced
*/
@DefaultConverter
@ConfigItem(defaultValue = "ENFORCING")
- PolicyEnforcerConfig.EnforcementMode enforcementMode;
+ public PolicyEnforcerConfig.EnforcementMode enforcementMode;
/**
* Defines a set of one or more claims that must be resolved and pushed to the Keycloak server in order to make
@@ -118,7 +118,7 @@ public static class PathConfig {
* claims available to policies
*/
@ConfigItem
- ClaimInformationPointConfig claimInformationPoint;
+ public ClaimInformationPointConfig claimInformationPoint;
}
@ConfigGroup
@@ -128,20 +128,20 @@ public static class MethodConfig {
* The name of the HTTP method
*/
@ConfigItem
- String method;
+ public String method;
/**
* An array of strings with the scopes associated with the method
*/
@ConfigItem
- List scopes;
+ public List scopes;
/**
* A string referencing the enforcement mode for the scopes associated with a method
*/
@DefaultConverter
@ConfigItem(defaultValue = "ALL")
- PolicyEnforcerConfig.ScopeEnforcementMode scopesEnforcementMode;
+ public PolicyEnforcerConfig.ScopeEnforcementMode scopesEnforcementMode;
}
@ConfigGroup
@@ -151,13 +151,13 @@ public static class PathCacheConfig {
* Defines the time in milliseconds when the entry should be expired
*/
@ConfigItem(defaultValue = "1000")
- int maxEntries = 1000;
+ public int maxEntries = 1000;
/**
* Defines the limit of entries that should be kept in the cache
*/
@ConfigItem(defaultValue = "30000")
- long lifespan = 30000;
+ public long lifespan = 30000;
}
@ConfigGroup
@@ -167,13 +167,13 @@ public static class ClaimInformationPointConfig {
*
*/
@ConfigItem(name = ConfigItem.PARENT)
- Map>> complexConfig;
+ public Map>> complexConfig;
/**
*
*/
@ConfigItem(name = ConfigItem.PARENT)
- Map> simpleConfig;
+ public Map> simpleConfig;
}
}
}
diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
index 78a8b5fb934fa..df2342cf3d153 100644
--- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
+++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java
@@ -1,6 +1,5 @@
package io.quarkus.keycloak.pep.runtime;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
@@ -23,8 +22,10 @@
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.security.credential.TokenCredential;
import io.quarkus.security.identity.SecurityIdentity;
+import io.quarkus.vertx.http.runtime.VertxInputStream;
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.impl.CookieImpl;
@@ -107,7 +108,16 @@ public Cookie getCookie(String cookieName) {
@Override
public String getHeader(String name) {
- return request.getHeader(name);
+ //TODO: this logic should be removed once KEYCLOAK-12412 is fixed
+ String value = request.getHeader(name);
+
+ if (name.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE.toString())) {
+ if (value.indexOf(';') != -1) {
+ return value.substring(0, value.indexOf(';'));
+ }
+ }
+
+ return value;
}
@Override
@@ -122,7 +132,17 @@ public InputStream getInputStream() {
@Override
public InputStream getInputStream(boolean buffered) {
- return new BufferedInputStream(new ByteArrayInputStream(routingContext.getBody().getBytes()));
+ try {
+ if (routingContext.getBody() != null) {
+ return new ByteArrayInputStream(routingContext.getBody().getBytes());
+ }
+ if (routingContext.request().isEnded()) {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ return new VertxInputStream(request);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
index d676b8ab814c2..ccd63565410d4 100644
--- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
+++ b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
@@ -1,5 +1,6 @@
package io.quarkus.resteasy.runtime.standalone;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -19,6 +20,7 @@
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.security.identity.CurrentIdentityAssociation;
import io.quarkus.vertx.http.runtime.CurrentVertxRequest;
+import io.quarkus.vertx.http.runtime.VertxInputStream;
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
import io.vertx.core.Context;
import io.vertx.core.Handler;
@@ -61,9 +63,13 @@ public VertxRequestHandler(Vertx vertx,
public void handle(RoutingContext request) {
// have to create input stream here. Cannot execute in another thread
// otherwise request handlers may not get set up before request ends
- VertxInputStream is;
+ InputStream is;
try {
- is = new VertxInputStream(request.request());
+ if (request.getBody() != null) {
+ is = new ByteArrayInputStream(request.getBody().getBytes());
+ } else {
+ is = new VertxInputStream(request.request());
+ }
} catch (IOException e) {
request.fail(e);
return;
diff --git a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java
index f9e7394440a1b..d66f8d71f3e48 100644
--- a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java
+++ b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java
@@ -353,7 +353,8 @@ public void run() {
return new Handler() {
@Override
public void handle(RoutingContext event) {
- VertxHttpExchange exchange = new VertxHttpExchange(event.request(), allocator, executorService, event);
+ VertxHttpExchange exchange = new VertxHttpExchange(event.request(), allocator, executorService, event,
+ event.getBody());
Optional maxBodySize = httpConfiguration.limits.maxBodySize;
if (maxBodySize.isPresent()) {
exchange.setMaxEntitySize(maxBodySize.get().asLongValue());
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java
new file mode 100644
index 0000000000000..c5743fcd30385
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java
@@ -0,0 +1,17 @@
+package io.quarkus.vertx.http.deployment;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+
+public final class BodyHandlerBuildItem extends SimpleBuildItem {
+ private final Handler handler;
+
+ public BodyHandlerBuildItem(Handler handler) {
+ this.handler = handler;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+}
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java
new file mode 100644
index 0000000000000..dbfd8541499a3
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java
@@ -0,0 +1,10 @@
+package io.quarkus.vertx.http.deployment;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * This is a marker that indicates that the body handler should be installed
+ * on all routes, as an extension requires the request to be fully buffered.
+ */
+public final class RequireBodyHandlerBuildItem extends MultiBuildItem {
+}
diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java
index 2dcebfca9208d..3f79ed6ee306c 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java
@@ -35,8 +35,10 @@
import io.quarkus.vertx.http.runtime.VertxHttpRecorder;
import io.quarkus.vertx.http.runtime.cors.CORSRecorder;
import io.quarkus.vertx.http.runtime.filters.Filter;
+import io.vertx.core.Handler;
import io.vertx.core.impl.VertxImpl;
import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
class VertxHttpProcessor {
@@ -97,6 +99,12 @@ VertxWebRouterBuildItem initializeRouter(VertxHttpRecorder recorder,
return new VertxWebRouterBuildItem(router);
}
+ @BuildStep
+ @Record(ExecutionTime.RUNTIME_INIT)
+ BodyHandlerBuildItem bodyHandler(VertxHttpRecorder recorder, HttpConfiguration httpConfiguration) {
+ return new BodyHandlerBuildItem(recorder.createBodyHandler(httpConfiguration));
+ }
+
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
ServiceStartBuildItem finalizeRouter(
@@ -106,7 +114,9 @@ ServiceStartBuildItem finalizeRouter(
List defaultRoutes, List filters,
VertxWebRouterBuildItem router, EventLoopCountBuildItem eventLoopCount,
HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration,
- BuildProducer reflectiveClass, List websocketSubProtocols)
+ BuildProducer reflectiveClass, List websocketSubProtocols,
+ List requireBodyHandlerBuildItems,
+ BodyHandlerBuildItem bodyHandlerBuildItem)
throws BuildException, IOException {
Optional defaultRoute;
if (defaultRoutes == null || defaultRoutes.isEmpty()) {
@@ -124,9 +134,14 @@ ServiceStartBuildItem finalizeRouter(
.filter(f -> f.getHandler() != null)
.map(FilterBuildItem::toFilter).collect(Collectors.toList());
+ //if the body handler is required then we know it is installed for all routes, so we don't need to register it here
+ Handler bodyHandler = !requireBodyHandlerBuildItems.isEmpty() ? bodyHandlerBuildItem.getHandler()
+ : null;
+
recorder.finalizeRouter(beanContainer.getValue(),
defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null),
- listOfFilters, vertx.getVertx(), router.getRouter(), httpBuildTimeConfig.rootPath, launchMode.getLaunchMode());
+ listOfFilters, vertx.getVertx(), router.getRouter(), httpBuildTimeConfig.rootPath, launchMode.getLaunchMode(),
+ !requireBodyHandlerBuildItems.isEmpty(), bodyHandler);
boolean startVirtual = requireVirtual.isPresent() || httpBuildTimeConfig.virtual;
if (startVirtual) {
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
index 4b7b13dfbc37a..69568666ea191 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java
@@ -35,6 +35,7 @@
import io.quarkus.runtime.Timing;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigInstantiator;
+import io.quarkus.runtime.configuration.MemorySize;
import io.quarkus.vertx.core.runtime.VertxCoreRecorder;
import io.quarkus.vertx.core.runtime.config.VertxConfiguration;
import io.quarkus.vertx.http.runtime.filters.Filter;
@@ -61,6 +62,7 @@
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
@Recorder
public class VertxHttpRecorder {
@@ -169,7 +171,8 @@ public void startServer(RuntimeValue vertxRuntimeValue, ShutdownContext s
public void finalizeRouter(BeanContainer container, Consumer defaultRouteHandler,
List filterList, RuntimeValue vertx,
- RuntimeValue runtimeValue, String rootPath, LaunchMode launchMode) {
+ RuntimeValue runtimeValue, String rootPath, LaunchMode launchMode, boolean requireBodyHandler,
+ Handler bodyHandler) {
// install the default route at the end
Router router = runtimeValue.getValue();
@@ -200,6 +203,18 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute
container.instance(RouterProducer.class).initialize(resumingRouter);
router.route().last().failureHandler(new QuarkusErrorHandler(launchMode.isDevOrTest()));
+ if (requireBodyHandler) {
+ //if this is set then everything needs the body handler installed
+ //TODO: config etc
+ router.route().order(Integer.MIN_VALUE).handler(new Handler() {
+ @Override
+ public void handle(RoutingContext routingContext) {
+ routingContext.request().resume();
+ bodyHandler.handle(routingContext);
+ }
+ });
+ }
+
if (rootPath.equals("/")) {
if (hotReplacementHandler != null) {
router.route().order(-1).handler(hotReplacementHandler);
@@ -626,4 +641,24 @@ public static Handler getRootHandler() {
return ACTUAL_ROOT;
}
+ public Handler createBodyHandler(HttpConfiguration httpConfiguration) {
+ BodyHandler bodyHandler = BodyHandler.create();
+ Optional maxBodySize = httpConfiguration.limits.maxBodySize;
+ if (maxBodySize.isPresent()) {
+ bodyHandler.setBodyLimit(maxBodySize.get().asLongValue());
+ }
+ final BodyConfig bodyConfig = httpConfiguration.body;
+ bodyHandler.setHandleFileUploads(bodyConfig.handleFileUploads);
+ bodyHandler.setUploadsDirectory(bodyConfig.uploadsDirectory);
+ bodyHandler.setDeleteUploadedFilesOnEnd(bodyConfig.deleteUploadedFilesOnEnd);
+ bodyHandler.setMergeFormAttributes(bodyConfig.mergeFormAttributes);
+ bodyHandler.setPreallocateBodyBuffer(bodyConfig.preallocateBodyBuffer);
+ return new Handler() {
+ @Override
+ public void handle(RoutingContext event) {
+ event.request().resume();
+ bodyHandler.handle(event);
+ }
+ };
+ }
}
diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxInputStream.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
similarity index 95%
rename from extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxInputStream.java
rename to extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
index 9c97bc82489f9..357f77de923aa 100644
--- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxInputStream.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
@@ -1,4 +1,4 @@
-package io.quarkus.resteasy.runtime.standalone;
+package io.quarkus.vertx.http.runtime;
import java.io.IOException;
import java.io.InputStream;
@@ -10,6 +10,7 @@
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
public class VertxInputStream extends InputStream {
@@ -80,6 +81,7 @@ public int available() throws IOException {
if (finished) {
return -1;
}
+
return exchange.readBytesAvailable();
}
@@ -199,7 +201,14 @@ public int readBytesAvailable() {
if (input1 != null) {
return input1.getByteBuf().readableBytes();
}
- return 0;
+
+ String length = request.getHeader(HttpHeaders.CONTENT_LENGTH);
+
+ if (length == null) {
+ return 0;
+ }
+
+ return Integer.parseInt(length);
}
}
diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java
index 715dbafcde91b..d8e3e968eef7b 100644
--- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java
+++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java
@@ -4,6 +4,10 @@
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
+/**
+ * use {@link io.quarkus.vertx.http.deployment.BodyHandlerBuildItem} instead
+ */
+@Deprecated
public final class BodyHandlerBuildItem extends SimpleBuildItem {
private final Handler handler;
diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
index 8b98af3df949c..ed69c72f5731e 100644
--- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
+++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
@@ -48,9 +48,9 @@
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.vertx.http.deployment.FilterBuildItem;
+import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;
-import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.web.Route;
import io.quarkus.vertx.web.RouteBase;
import io.quarkus.vertx.web.RouteFilter;
@@ -149,9 +149,8 @@ void validateBeanDeployment(
}
@BuildStep
- @Record(ExecutionTime.RUNTIME_INIT)
- BodyHandlerBuildItem bodyHandler(VertxWebRecorder recorder, HttpConfiguration httpConfiguration) {
- return new BodyHandlerBuildItem(recorder.createBodyHandler(httpConfiguration));
+ BodyHandlerBuildItem bodyHandler(io.quarkus.vertx.http.deployment.BodyHandlerBuildItem realOne) {
+ return new BodyHandlerBuildItem(realOne.getHandler());
}
@BuildStep
@@ -163,9 +162,10 @@ void addAdditionalRoutes(
BuildProducer generatedClass,
AnnotationProxyBuildItem annotationProxy,
BuildProducer reflectiveClasses,
- BodyHandlerBuildItem bodyHandler,
+ io.quarkus.vertx.http.deployment.BodyHandlerBuildItem bodyHandler,
BuildProducer routeProducer,
- BuildProducer filterProducer) throws IOException {
+ BuildProducer filterProducer,
+ List bodyHandlerRequired) throws IOException {
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClass, true);
diff --git a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java
index ffc08969aa4fb..e7b3051717b6b 100644
--- a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java
+++ b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java
@@ -1,20 +1,15 @@
package io.quarkus.vertx.web.runtime;
import java.lang.reflect.InvocationTargetException;
-import java.util.Optional;
import java.util.function.Function;
import io.quarkus.runtime.annotations.Recorder;
-import io.quarkus.runtime.configuration.MemorySize;
-import io.quarkus.vertx.http.runtime.BodyConfig;
-import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.http.runtime.RouterProducer;
import io.quarkus.vertx.web.Route;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
-import io.vertx.ext.web.handler.BodyHandler;
@Recorder
public class VertxWebRecorder {
@@ -66,33 +61,14 @@ public io.vertx.ext.web.Route apply(Router router) {
route.consumes(consumes);
}
}
- route.handler(bodyHandler);
+ if (bodyHandler != null) {
+ route.handler(bodyHandler);
+ }
return route;
}
};
}
- public Handler createBodyHandler(HttpConfiguration httpConfiguration) {
- BodyHandler bodyHandler = BodyHandler.create();
- Optional maxBodySize = httpConfiguration.limits.maxBodySize;
- if (maxBodySize.isPresent()) {
- bodyHandler.setBodyLimit(maxBodySize.get().asLongValue());
- }
- final BodyConfig bodyConfig = httpConfiguration.body;
- bodyHandler.setHandleFileUploads(bodyConfig.handleFileUploads);
- bodyHandler.setUploadsDirectory(bodyConfig.uploadsDirectory);
- bodyHandler.setDeleteUploadedFilesOnEnd(bodyConfig.deleteUploadedFilesOnEnd);
- bodyHandler.setMergeFormAttributes(bodyConfig.mergeFormAttributes);
- bodyHandler.setPreallocateBodyBuffer(bodyConfig.preallocateBodyBuffer);
- return new Handler() {
- @Override
- public void handle(RoutingContext event) {
- event.request().resume();
- bodyHandler.handle(event);
- }
- };
- }
-
private String ensureStartWithSlash(String path) {
if (path.startsWith("/")) {
return path;
diff --git a/integration-tests/keycloak-authorization/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java b/integration-tests/keycloak-authorization/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java
index a3ebd29a35ced..beb96fe320251 100644
--- a/integration-tests/keycloak-authorization/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java
+++ b/integration-tests/keycloak-authorization/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java
@@ -1,20 +1,26 @@
package io.quarkus.it.keycloak;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.security.auth.AuthPermission;
+import javax.ws.rs.Consumes;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.keycloak.representations.idm.authorization.Permission;
import io.quarkus.security.identity.SecurityIdentity;
+import io.vertx.core.http.HttpServerRequest;
@Path("/api/permission")
public class ProtectedResource {
@@ -47,4 +53,15 @@ public List claimProtected() {
public List httpResponseClaimProtected() {
return identity.getAttribute("permissions");
}
+
+ @Path("/body-claim")
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public List bodyClaim(Map body, @Context HttpServerRequest request) {
+ if (body == null && !body.containsKey("from-body")) {
+ return Collections.emptyList();
+ }
+ return identity.getAttribute("permissions");
+ }
}
diff --git a/integration-tests/keycloak-authorization/src/main/resources/application.properties b/integration-tests/keycloak-authorization/src/main/resources/application.properties
index 1aef8e6e968db..2989b9924ea5d 100644
--- a/integration-tests/keycloak-authorization/src/main/resources/application.properties
+++ b/integration-tests/keycloak-authorization/src/main/resources/application.properties
@@ -32,3 +32,8 @@ quarkus.keycloak.policy-enforcer.paths.3.claim-information-point.http.headers.Au
# Disables policy enforcement for a path
quarkus.keycloak.policy-enforcer.paths.4.path=/api/public
quarkus.keycloak.policy-enforcer.paths.4.enforcement-mode=DISABLED
+
+# Defines a claim which value is based on the response from an external service
+quarkus.keycloak.policy-enforcer.paths.5.path=/api/permission/body-claim
+quarkus.keycloak.policy-enforcer.paths.5.claim-information-point.claims.from-body={request.body['/from-body']}
+
diff --git a/integration-tests/keycloak-authorization/src/test/java/io/quarkus/it/keycloak/PolicyEnforcerTest.java b/integration-tests/keycloak-authorization/src/test/java/io/quarkus/it/keycloak/PolicyEnforcerTest.java
index 0b87ae3d1a65f..8612f498e253a 100644
--- a/integration-tests/keycloak-authorization/src/test/java/io/quarkus/it/keycloak/PolicyEnforcerTest.java
+++ b/integration-tests/keycloak-authorization/src/test/java/io/quarkus/it/keycloak/PolicyEnforcerTest.java
@@ -26,6 +26,7 @@
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
/**
* @author Pedro Igor
@@ -116,6 +117,7 @@ private static ClientRepresentation createClient(String clientId) {
configurePermissionResourcePermission(authorizationSettings);
configureClaimBasedPermission(authorizationSettings);
configureHttpResponseClaimBasedPermission(authorizationSettings);
+ configureBodyClaimBasedPermission(authorizationSettings);
client.setAuthorizationSettings(authorizationSettings);
@@ -155,6 +157,20 @@ private static void configureHttpResponseClaimBasedPermission(ResourceServerRepr
"/api/permission/http-response-claim-protected"), policy);
}
+ private static void configureBodyClaimBasedPermission(ResourceServerRepresentation settings) {
+ PolicyRepresentation policy = createJSPolicy("Body Claim-Based Policy",
+ "var context = $evaluation.getContext();\n"
+ + "print(context.getAttributes().toMap());"
+ + "var attributes = context.getAttributes();\n"
+ + "\n"
+ + "if (attributes.containsValue('from-body', 'grant')) {\n"
+ + " $evaluation.grant();\n"
+ + "}",
+ settings);
+ createPermission(settings, createResource(settings, "Body Claim Protected Resource",
+ "/api/permission/body-claim"), policy);
+ }
+
private static void createPermission(ResourceServerRepresentation settings, ResourceRepresentation resource,
PolicyRepresentation policy) {
PolicyRepresentation permission = new PolicyRepresentation();
@@ -260,6 +276,18 @@ public void testHttpResponseFromExternalServiceAsClaim() {
.statusCode(403);
}
+ @Test
+ public void testBodyClaim() {
+ RestAssured.given().auth().oauth2(getAccessToken("alice"))
+ .contentType(ContentType.JSON)
+ .body("{\"from-body\": \"grant\"}")
+ .when()
+ .post("/api/permission/body-claim")
+ .then()
+ .statusCode(200)
+ .and().body(Matchers.containsString("Body Claim Protected Resource"));
+ }
+
@Test
public void testPublicResource() {
RestAssured.given()