You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ld...@apache.org on 2026/06/11 21:14:58 UTC
(camel) 03/03: CAMEL-23723: Fail closed when the rest-openapi delegate does not enforce oauthProfile
This is an automated email from the ASF dual-hosted git repository.
luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 37ade6028080657e2402791cb8950e16f9828464
Author: Luigi De Masi <ld...@redhat.com>
AuthorDate: Thu Jun 11 17:13:34 2026 +0200
CAMEL-23723: Fail closed when the rest-openapi delegate does not enforce oauthProfile
Adds RestOpenApiConsumerFactory.supportsOAuthProfile(), defaulting to false,
so rest-openapi fails at startup instead of creating an unprotected consumer
when oauthProfile is set against a delegate that does not enforce it.
PlatformHttpComponent declares support. Factories that enforce the option
must override the method to return true.
Co-authored-by: Claude Opus 4.6 <no...@anthropic.com>
---
.../platform/http/PlatformHttpComponent.java | 6 +++
.../src/main/docs/rest-openapi-component.adoc | 8 ++--
.../rest/openapi/RestOpenApiEndpoint.java | 8 ++++
.../rest/openapi/RestOpenApiEndpointV3Test.java | 49 ++++++++++++++++++++++
.../vertx/http/VertxHttpTransferExceptionTest.java | 2 -
.../camel/spi/RestOpenApiConsumerFactory.java | 12 ++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 8 ++--
.../modules/ROOT/pages/rest-dsl-openapi.adoc | 5 ++-
8 files changed, 88 insertions(+), 10 deletions(-)
diff --git a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java
index 2ac0a76b8afb..df2bdabc8030 100644
--- a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java
+++ b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java
@@ -127,6 +127,12 @@ public class PlatformHttpComponent extends HeaderFilterStrategyComponent
parameters, true, false);
}
+ @Override
+ public boolean supportsOAuthProfile() {
+ // the oauthProfile endpoint option is enforced by the platform-http consumer
+ return true;
+ }
+
/**
* Adds a known http endpoint managed by this component.
*/
diff --git a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
index 2988682af8e0..b09f86f2d0d8 100644
--- a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
+++ b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
@@ -139,9 +139,11 @@ OpenAPI `securitySchemes` and operation security requirements are not converted
configuration; select the OAuth profile explicitly with the REST endpoint property or direct endpoint
URI option.
The `oauthProfile` option is a first-class `rest-openapi` endpoint option that is forwarded to the
-selected delegate, which is responsible for enforcing it. If the delegate ignores the option,
-the endpoint can start without the expected protection, so verify that the selected consumer
-component supports `oauthProfile`.
+selected delegate, which is responsible for enforcing it. The route fails at startup when the
+resolved `RestOpenApiConsumerFactory` does not declare that its consumers enforce `oauthProfile`,
+so a misconfigured delegate cannot start without the expected protection. Custom factories that
+enforce the option must override `RestOpenApiConsumerFactory.supportsOAuthProfile()` to return
+`true`.
== Request validation
diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
index 039826116e64..11f5b98a5fd4 100644
--- a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
+++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
@@ -327,6 +327,14 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint {
}
if (factory != null) {
+ // fail closed: never start an unprotected consumer when oauthProfile is configured but the
+ // delegate factory does not declare that its consumers enforce it
+ if (isNotEmpty(oauthProfile) && !factory.supportsOAuthProfile()) {
+ throw new IllegalArgumentException(
+ "The oauthProfile option is not supported by the resolved RestOpenApiConsumerFactory ("
+ + factory.getClass().getName()
+ + "); select a consumer component that enforces oauthProfile");
+ }
RestConfiguration config = CamelContextHelper.getRestConfiguration(getCamelContext(), cname);
Map<String, Object> copy = new HashMap<>(parameters); // defensive copy of the parameters
// pass oauthProfile to the delegate consumer, which is responsible for enforcing it
diff --git a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java
index c15f84e5c6b5..24ed458fb49f 100644
--- a/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java
+++ b/components/camel-rest-openapi/src/test/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointV3Test.java
@@ -124,6 +124,33 @@ public class RestOpenApiEndpointV3Test {
}
}
+ @Test
+ public void oauthProfileFailsClosedWhenDelegateDoesNotSupportIt() throws Exception {
+ final CamelContext camelContext = new DefaultCamelContext();
+ final RestOpenApiComponent component = new RestOpenApiComponent(camelContext);
+ String specificationUri = Objects.requireNonNull(RestOpenApiEndpointV3Test.class.getResource("/openapi-v3.json"))
+ .toString();
+ camelContext.addComponent("rest-openapi", component);
+ camelContext.addComponent("no-oauth-http", new NoOAuthRestOpenApiConsumerFactory());
+ camelContext.start();
+
+ try {
+ RestOpenApiEndpoint endpoint = camelContext.getEndpoint(
+ "rest-openapi:" + specificationUri + "?consumerComponentName=no-oauth-http&oauthProfile=myprofile",
+ RestOpenApiEndpoint.class);
+
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> endpoint.createConsumer(exchange -> {
+ }));
+
+ assertThat(exception.getMessage())
+ .contains("The oauthProfile option is not supported by the resolved RestOpenApiConsumerFactory")
+ .contains(NoOAuthRestOpenApiConsumerFactory.class.getName());
+ } finally {
+ camelContext.stop();
+ }
+ }
+
@Test
public void unknownEndpointPropertyIsTolerated() throws Exception {
final CamelContext camelContext = new DefaultCamelContext();
@@ -516,6 +543,28 @@ public class RestOpenApiEndpointV3Test {
this.parameters = new HashMap<>(parameters);
return mock(Consumer.class);
}
+
+ @Override
+ public boolean supportsOAuthProfile() {
+ return true;
+ }
+ }
+
+ private static final class NoOAuthRestOpenApiConsumerFactory extends DefaultComponent
+ implements RestOpenApiConsumerFactory {
+
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Consumer createConsumer(
+ CamelContext camelContext, Processor processor, String contextPath, RestConfiguration configuration,
+ Map<String, Object> parameters) {
+ return mock(Consumer.class);
+ }
+ // does not override supportsOAuthProfile: the default is false
}
}
diff --git a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpTransferExceptionTest.java b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpTransferExceptionTest.java
index 1d3e2b267e33..42dd4b0c60ee 100644
--- a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpTransferExceptionTest.java
+++ b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpTransferExceptionTest.java
@@ -22,8 +22,6 @@ import org.apache.camel.builder.RouteBuilder;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class VertxHttpTransferExceptionTest extends VertxHttpTestSupport {
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java
index 292392705bc1..807cfa37077a 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java
@@ -50,4 +50,16 @@ public interface RestOpenApiConsumerFactory {
CamelContext camelContext, Processor processor, String contextPath,
RestConfiguration configuration, Map<String, Object> parameters)
throws Exception;
+
+ /**
+ * Whether consumers created by this factory enforce the {@code oauthProfile} option for validating incoming
+ * {@code Authorization: Bearer} tokens. Factories that do not enforce the option must return {@code false}, so
+ * callers such as the rest-openapi endpoint can fail fast at startup instead of starting an unprotected consumer.
+ *
+ * @return true when consumers created by this factory enforce the {@code oauthProfile} option
+ * @since 4.21
+ */
+ default boolean supportsOAuthProfile() {
+ return false;
+ }
}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index fe9c4f4d676c..60ba22b5cf99 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -877,9 +877,11 @@ Direct contract-first OpenAPI consumer routes can also pass the option on the en
`rest-openapi:petstore-v3.json?consumerComponentName=platform-http&oauthProfile=myprofile`.
OpenAPI `securitySchemes` are not converted into `oauthProfile` automatically.
The `oauthProfile` option is a first-class `rest-openapi` endpoint option that is forwarded to the
-selected consumer delegate, which is responsible for enforcing it. Verify that the selected delegate
-supports `oauthProfile`; delegates that ignore the option can otherwise start without the expected
-endpoint protection.
+selected consumer delegate, which is responsible for enforcing it. The route fails at startup when
+the resolved `RestOpenApiConsumerFactory` does not declare that its consumers enforce `oauthProfile`
+via the new `RestOpenApiConsumerFactory.supportsOAuthProfile()` method, which defaults to `false`.
+Custom factories that enforce the option must override it to return `true`; the built-in
+`platform-http` delegate declares support.
For netty-http consumers, `oauthProfile` requires `usingExecutorService=true` and `sync=true`, which
are the defaults, so blocking validation does not run on a Netty event-loop thread and rejection
responses can be returned to the client. Routes that combine `oauthProfile`
diff --git a/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc b/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
index ff6ed3055fba..9983bcaa769f 100644
--- a/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
+++ b/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
@@ -66,8 +66,9 @@ for example `rest-openapi:petstore-v3.json?consumerComponentName=platform-http&o
Consumer endpoint URIs identify the OpenAPI specification and do not include an `#operationId`
fragment.
The `oauthProfile` option is forwarded to the selected delegate, which is responsible for
-enforcing it. Verify that the selected consumer component supports `oauthProfile`; a delegate
-that ignores the option starts without the expected protection.
+enforcing it. The route fails at startup when the resolved `RestOpenApiConsumerFactory` does not
+declare that its consumers enforce `oauthProfile`, so a misconfigured delegate cannot start
+without the expected protection.
== Contract first