This is an automated email from the ASF dual-hosted git repository. cdeppisch pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 1babc70f2a2 CAMEL-21900: Verify OIDC token on Knative service endpoint 1babc70f2a2 is described below commit 1babc70f2a20da42e4af89fd28951c7bd83580ed Author: Christoph Deppisch <cdeppi...@redhat.com> AuthorDate: Wed Mar 26 15:30:52 2025 +0100 CAMEL-21900: Verify OIDC token on Knative service endpoint - Add Knative service options to enable OIDC support when receiving Knative events - OIDC service options define OIDC token path - Enhance Knative Http service to verify OIDC Authorization headers to match given token - Make Knative OIDC service options configurable via environment settings --- .../camel/component/knative/KnativeEndpoint.java | 3 - .../knative/http/KnativeHttpConsumer.java | 25 ++++ .../knative/http/KnativeHttpConsumerFactory.java | 22 +++ .../knative/http/KnativeHttpServiceOptions.java | 26 ++++ .../component/knative/http/KnativeHttpSupport.java | 16 +++ .../knative/http/KnativeOidcClientOptions.java | 23 +-- ...Options.java => KnativeOidcServiceOptions.java} | 58 +++----- .../component/knative/http/KnativeHttpTest.java | 154 +++++++++++++++++++++ 8 files changed, 270 insertions(+), 57 deletions(-) diff --git a/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/KnativeEndpoint.java b/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/KnativeEndpoint.java index 151d3ed8e3a..00c7511eb52 100644 --- a/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/KnativeEndpoint.java +++ b/components/camel-knative/camel-knative-component/src/main/java/org/apache/camel/component/knative/KnativeEndpoint.java @@ -131,9 +131,6 @@ public class KnativeEndpoint extends DefaultEndpoint { Consumer consumer = getComponent().getOrCreateConsumerFactory().createConsumer(this, createTransportConfiguration(service), service, pipeline); - // signal that this path is exposed for knative - String path = service.getPath(); - PropertyBindingSupport.build() .withCamelContext(camelContext) .withProperties(configuration.getTransportOptions()) diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumer.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumer.java index a8ee6485f61..8d23a83d7b4 100644 --- a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumer.java +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumer.java @@ -24,6 +24,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; @@ -36,6 +37,7 @@ import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.Processor; +import org.apache.camel.RuntimeCamelException; import org.apache.camel.TypeConverter; import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedResource; @@ -59,6 +61,7 @@ public class KnativeHttpConsumer extends DefaultConsumer { private final KnativeTransportConfiguration configuration; private final Predicate<HttpServerRequest> filter; private final KnativeResource resource; + private final KnativeHttpServiceOptions serviceOptions; private final Supplier<Router> router; private final HeaderFilterStrategy headerFilterStrategy; private volatile String path; @@ -72,11 +75,13 @@ public class KnativeHttpConsumer extends DefaultConsumer { Endpoint endpoint, KnativeResource resource, Supplier<Router> router, + KnativeHttpServiceOptions serviceOptions, Processor processor) { super(endpoint, processor); this.configuration = configuration; this.resource = resource; this.router = router; + this.serviceOptions = serviceOptions; this.headerFilterStrategy = new KnativeHttpHeaderFilterStrategy(); this.filter = KnativeHttpSupport.createFilter(this.configuration.getCloudEvent(), resource); this.preallocateBodyBuffer = true; @@ -142,6 +147,26 @@ public class KnativeHttpConsumer extends DefaultConsumer { bodyHandler.setBodyLimit(this.maxBodySize.longValueExact()); } + // add OIDC token verification handler + if (serviceOptions instanceof KnativeOidcServiceOptions oidcServiceOptions && + oidcServiceOptions.isOidcEnabled()) { + route.handler(routingContext -> { + if (routingContext.request().headers().contains(HttpHeaders.AUTHORIZATION)) { + String auth = routingContext.request().getHeader(HttpHeaders.AUTHORIZATION); + String token = oidcServiceOptions.retrieveOidcToken(); + if (("Bearer " + token).equals(auth)) { + routingContext.next(); + } else { + routingContext.fail(401, new RuntimeCamelException("OIDC request verification failed - forbidden")); + } + } else { + routingContext.fail(401, new RuntimeCamelException( + "OIDC request verification failed - " + + "missing proper authorization token")); + } + }); + } + // add body handler route.handler((RoutingContext event) -> { event.request().resume(); diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumerFactory.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumerFactory.java index b05719bb22e..f268d76412e 100644 --- a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumerFactory.java +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpConsumerFactory.java @@ -33,6 +33,7 @@ import org.apache.camel.support.service.ServiceSupport; public class KnativeHttpConsumerFactory extends ServiceSupport implements CamelContextAware, KnativeConsumerFactory { private Router router; private CamelContext camelContext; + private KnativeHttpServiceOptions serviceOptions; public KnativeHttpConsumerFactory() { } @@ -41,6 +42,14 @@ public class KnativeHttpConsumerFactory extends ServiceSupport implements CamelC this.camelContext = camelContext; } + @Override + protected void doInit() throws Exception { + if (serviceOptions == null) { + KnativeHttpSupport.lookupServiceOptions(camelContext) + .ifPresent(options -> serviceOptions = options); + } + } + public Router getRouter() { return router; } @@ -54,6 +63,18 @@ public class KnativeHttpConsumerFactory extends ServiceSupport implements CamelC return this; } + public KnativeHttpServiceOptions getServiceOptions() { + return serviceOptions; + } + + public void setServiceOptions(KnativeHttpServiceOptions serviceOptions) { + if (ServiceHelper.isStarted(this)) { + throw new IllegalArgumentException("Can't set the service options after the service has been started"); + } + + this.serviceOptions = serviceOptions; + } + @Override public void setCamelContext(CamelContext camelContext) { this.camelContext = camelContext; @@ -72,6 +93,7 @@ public class KnativeHttpConsumerFactory extends ServiceSupport implements CamelC endpoint, service, this::lookupRouter, + serviceOptions, processor); } diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpServiceOptions.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpServiceOptions.java new file mode 100644 index 00000000000..b3fba587ebb --- /dev/null +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpServiceOptions.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.camel.component.knative.http; + +import org.apache.camel.CamelContextAware; + +/** + * Interface specifying Knative service options applied to the exposed Http service. + */ +public interface KnativeHttpServiceOptions extends CamelContextAware { +} diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpSupport.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpSupport.java index a9665be83ec..f0abeea3c4a 100644 --- a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpSupport.java +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeHttpSupport.java @@ -156,4 +156,20 @@ public final class KnativeHttpSupport { return Optional.empty(); } + + /** + * Retrieve service options from given CamelContext. + * + * @param camelContext the current context. + * @return service options or empty + */ + public static Optional<KnativeHttpServiceOptions> lookupServiceOptions(CamelContext camelContext) { + KnativeHttpServiceOptions serviceOptions + = CamelContextHelper.findSingleByType(camelContext, KnativeHttpServiceOptions.class); + if (serviceOptions != null) { + return Optional.of(serviceOptions); + } + + return Optional.empty(); + } } diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java index ebd278f6339..3e38e87db1d 100644 --- a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java @@ -20,7 +20,6 @@ package org.apache.camel.component.knative.http; import java.io.IOException; import java.util.Optional; -import io.vertx.ext.web.client.OAuth2WebClientOptions; import org.apache.camel.CamelContext; import org.apache.camel.spi.PropertiesComponent; import org.apache.camel.support.ResourceHelper; @@ -37,8 +36,6 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { private static final String PROPERTY_PREFIX = "camel.knative.client.oidc."; - private OAuth2WebClientOptions oAuth2ClientOptions; - private boolean oidcEnabled; private String oidcTokenPath; @@ -47,6 +44,8 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { private boolean cacheTokens = true; + private boolean renewTokenOnForbidden = true; + public KnativeOidcClientOptions() { } @@ -61,10 +60,6 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { public void configureOptions(CamelContext camelContext) { super.configureOptions(camelContext); - if (oAuth2ClientOptions == null) { - oAuth2ClientOptions = new OAuth2WebClientOptions().setRenewTokenOnForbidden(true); - } - PropertiesComponent propertiesComponent = camelContext.getPropertiesComponent(); boolean oidcEnabled = Boolean.parseBoolean( @@ -78,7 +73,7 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { boolean renewTokenOnForbidden = Boolean.parseBoolean( propertiesComponent.resolveProperty(PROPERTY_PREFIX + "renew.tokens.on.forbidden").orElse("true")); - oAuth2ClientOptions.setRenewTokenOnForbidden(renewTokenOnForbidden); + setRenewTokenOnForbidden(renewTokenOnForbidden); boolean cacheTokens = Boolean.parseBoolean( propertiesComponent.resolveProperty(PROPERTY_PREFIX + "cache.tokens").orElse("true")); @@ -137,18 +132,10 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { } public void setRenewTokenOnForbidden(boolean enabled) { - this.oAuth2ClientOptions.setRenewTokenOnForbidden(enabled); + this.renewTokenOnForbidden = enabled; } public boolean isRenewTokenOnForbidden() { - return this.oAuth2ClientOptions.isRenewTokenOnForbidden(); - } - - public void setOAuth2ClientOptions(OAuth2WebClientOptions oAuth2ClientOptions) { - this.oAuth2ClientOptions = oAuth2ClientOptions; - } - - public OAuth2WebClientOptions getOAuth2ClientOptions() { - return oAuth2ClientOptions; + return this.renewTokenOnForbidden; } } diff --git a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcServiceOptions.java similarity index 66% copy from components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java copy to components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcServiceOptions.java index ebd278f6339..1c2c54f274a 100644 --- a/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcClientOptions.java +++ b/components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcServiceOptions.java @@ -20,24 +20,19 @@ package org.apache.camel.component.knative.http; import java.io.IOException; import java.util.Optional; -import io.vertx.ext.web.client.OAuth2WebClientOptions; import org.apache.camel.CamelContext; import org.apache.camel.spi.PropertiesComponent; import org.apache.camel.support.ResourceHelper; import org.apache.camel.util.IOHelper; /** - * Knative client options are able to autoconfigure OpenID Connect authentication with the use of an access token. The - * path to the OIDC access token is configurable via system property or environment variable settings. Usually the token - * is mounted by a Kubernetes volume and gets refreshed over time. The options provide a procedure to renew the token. - * Basically that means reading the token again from the given path to get the updated value. As an alternative to that - * you can disable token caching so the token is read for each request. + * OpenID connect service options implementation grants access to the OIDC token that should be used to verify client + * requests. */ -public class KnativeOidcClientOptions extends KnativeSslClientOptions { +public class KnativeOidcServiceOptions implements KnativeHttpServiceOptions { + private static final String PROPERTY_PREFIX = "camel.knative.service.oidc."; - private static final String PROPERTY_PREFIX = "camel.knative.client.oidc."; - - private OAuth2WebClientOptions oAuth2ClientOptions; + private CamelContext camelContext; private boolean oidcEnabled; @@ -47,11 +42,18 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { private boolean cacheTokens = true; - public KnativeOidcClientOptions() { + public KnativeOidcServiceOptions() { + } + + public KnativeOidcServiceOptions(CamelContext camelContext) { + this.camelContext = camelContext; + configureOptions(camelContext); } - public KnativeOidcClientOptions(CamelContext camelContext) { - super(camelContext); + public void configureOptions() { + if (camelContext != null) { + configureOptions(camelContext); + } } /** @@ -59,12 +61,6 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { * Camel context. */ public void configureOptions(CamelContext camelContext) { - super.configureOptions(camelContext); - - if (oAuth2ClientOptions == null) { - oAuth2ClientOptions = new OAuth2WebClientOptions().setRenewTokenOnForbidden(true); - } - PropertiesComponent propertiesComponent = camelContext.getPropertiesComponent(); boolean oidcEnabled = Boolean.parseBoolean( @@ -75,11 +71,6 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { Optional<String> oidcTokenPath = propertiesComponent.resolveProperty(PROPERTY_PREFIX + "token.path"); oidcTokenPath.ifPresent(token -> this.oidcTokenPath = token); - boolean renewTokenOnForbidden = Boolean.parseBoolean( - propertiesComponent.resolveProperty(PROPERTY_PREFIX + "renew.tokens.on.forbidden").orElse("true")); - - oAuth2ClientOptions.setRenewTokenOnForbidden(renewTokenOnForbidden); - boolean cacheTokens = Boolean.parseBoolean( propertiesComponent.resolveProperty(PROPERTY_PREFIX + "cache.tokens").orElse("true")); setCacheTokens(cacheTokens); @@ -136,19 +127,14 @@ public class KnativeOidcClientOptions extends KnativeSslClientOptions { return oidcTokenPath; } - public void setRenewTokenOnForbidden(boolean enabled) { - this.oAuth2ClientOptions.setRenewTokenOnForbidden(enabled); - } - - public boolean isRenewTokenOnForbidden() { - return this.oAuth2ClientOptions.isRenewTokenOnForbidden(); - } - - public void setOAuth2ClientOptions(OAuth2WebClientOptions oAuth2ClientOptions) { - this.oAuth2ClientOptions = oAuth2ClientOptions; + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + configureOptions(); } - public OAuth2WebClientOptions getOAuth2ClientOptions() { - return oAuth2ClientOptions; + @Override + public CamelContext getCamelContext() { + return camelContext; } } diff --git a/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java index e138f80c247..cf64d5c696f 100644 --- a/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java +++ b/components/camel-knative/camel-knative-http/src/test/java/org/apache/camel/component/knative/http/KnativeHttpTest.java @@ -2476,4 +2476,158 @@ public class KnativeHttpTest { server.stop(); } } + + @ParameterizedTest + @EnumSource(CloudEvents.class) + void testOidcServiceOptions(CloudEvent ce) throws Exception { + KnativeComponent component = configureKnativeComponent( + context, + ce, + sourceEvent( + "default", + Map.of( + Knative.KNATIVE_CLOUD_EVENT_TYPE, "myEvent", + Knative.CONTENT_TYPE, "text/plain"))); + + KnativeOidcServiceOptions serviceOptions = new KnativeOidcServiceOptions(context); + serviceOptions.setOidcEnabled(true); + serviceOptions.setOidcTokenPath("classpath:oidc/token.txt"); + + if (component.getConsumerFactory() instanceof KnativeHttpConsumerFactory consumerFactory) { + consumerFactory.setServiceOptions(serviceOptions); + } + + RouteBuilder.addRoutes(context, b -> { + b.from("knative:event/myEvent") + .to("mock:ce"); + }); + + context.start(); + + MockEndpoint mock = context.getEndpoint("mock:ce", MockEndpoint.class); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_VERSION, ce.version()); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_TYPE, "myEvent"); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_ID, "myEventID"); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_SOURCE, "/somewhere"); + mock.expectedHeaderReceived(Exchange.CONTENT_TYPE, "text/plain"); + mock.expectedMessagesMatches(e -> e.getMessage().getHeaders().containsKey(CloudEvent.CAMEL_CLOUD_EVENT_TIME)); + mock.expectedBodiesReceived("test"); + mock.expectedMessageCount(1); + + given() + .body("test") + .header(Exchange.CONTENT_TYPE, "text/plain") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_VERSION), ce.version()) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TYPE), "myEvent") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_ID), "myEventID") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TIME), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_SOURCE), "/somewhere") + .when() + .post() + .then() + .statusCode(401); // forbidden - missing token + + given() + .body("test") + .header(Exchange.CONTENT_TYPE, "text/plain") + .header("Authorization", "Bearer wrong_token") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_VERSION), ce.version()) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TYPE), "myEvent") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_ID), "myEventID") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TIME), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_SOURCE), "/somewhere") + .when() + .post() + .then() + .statusCode(401); // forbidden - wrong token + + given() + .body("test") + .header(Exchange.CONTENT_TYPE, "text/plain") + .header("Authorization", "Bearer whatever_the_token_is") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_VERSION), ce.version()) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TYPE), "myEvent") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_ID), "myEventID") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TIME), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_SOURCE), "/somewhere") + .when() + .post() + .then() + .statusCode(200); + + mock.assertIsSatisfied(); + } + + @ParameterizedTest + @EnumSource(CloudEvents.class) + void testOidcServiceOptionsPropertyConf(CloudEvent ce) throws Exception { + context.getPropertiesComponent().addInitialProperty("camel.knative.service.oidc.enabled", "true"); + context.getPropertiesComponent().addInitialProperty("camel.knative.service.oidc.token.path", + "classpath:oidc/token.txt"); + + KnativeComponent component = configureKnativeComponent( + context, + ce, + sourceEvent( + "default", + Map.of( + Knative.KNATIVE_CLOUD_EVENT_TYPE, "myEvent", + Knative.CONTENT_TYPE, "text/plain"))); + + RouteBuilder.addRoutes(context, b -> { + b.from("knative:event/myEvent") + .to("mock:ce"); + }); + + PropertyBindingSupport.build().bind(context, component, + "camel.component.knative.consumerFactory.serviceOptions", + "#class:" + KnativeOidcServiceOptions.class.getName()); + + context.start(); + + MockEndpoint mock = context.getEndpoint("mock:ce", MockEndpoint.class); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_VERSION, ce.version()); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_TYPE, "myEvent"); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_ID, "myEventID"); + mock.expectedHeaderReceived(CloudEvent.CAMEL_CLOUD_EVENT_SOURCE, "/somewhere"); + mock.expectedHeaderReceived(Exchange.CONTENT_TYPE, "text/plain"); + mock.expectedMessagesMatches(e -> e.getMessage().getHeaders().containsKey(CloudEvent.CAMEL_CLOUD_EVENT_TIME)); + mock.expectedBodiesReceived("test"); + mock.expectedMessageCount(1); + + given() + .body("test") + .header(Exchange.CONTENT_TYPE, "text/plain") + .header("Authorization", "Bearer wrong_token") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_VERSION), ce.version()) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TYPE), "myEvent") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_ID), "myEventID") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TIME), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_SOURCE), "/somewhere") + .when() + .post() + .then() + .statusCode(401); // forbidden - wrong token + + given() + .body("test") + .header(Exchange.CONTENT_TYPE, "text/plain") + .header("Authorization", "Bearer whatever_the_token_is") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_VERSION), ce.version()) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TYPE), "myEvent") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_ID), "myEventID") + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_TIME), + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.now())) + .header(httpAttribute(ce, CloudEvent.CAMEL_CLOUD_EVENT_SOURCE), "/somewhere") + .when() + .post() + .then() + .statusCode(200); + + mock.assertIsSatisfied(); + } }