diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a3a2b7a8ce871..c5c10f5c532ea 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -30,7 +30,7 @@ 1.6.0 1.6.0-alpha 1.6.0 - 4.1.1 + 4.1.2 1.7.4 0.22.0 2.0 diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java index 40457d081d364..ffabb59f52399 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java @@ -42,6 +42,7 @@ public final class OidcUtils { * ignoring those which are located inside a pair of the double quotes. */ private static final Pattern CLAIM_PATH_PATTERN = Pattern.compile("\\/(?=(?:(?:[^\"]*\"){2})*[^\"]*$)"); + public static final String QUARKUS_IDENTITY_EXPIRE_TIME = "quarkus.identity.expire-time"; private OidcUtils() { @@ -163,6 +164,7 @@ static QuarkusSecurityIdentity validateAndCreateIdentity( } catch (InvalidJwtException e) { throw new AuthenticationFailedException(e); } + builder.addAttribute(QUARKUS_IDENTITY_EXPIRE_TIME, jwtPrincipal.getExpirationTime()); builder.setPrincipal(jwtPrincipal); setSecurityIdentityRoles(builder, config, rolesJson); setSecurityIdentityUserInfo(builder, userInfo); diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java index 6b3fb5996d6b2..41d18aa491ee6 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java @@ -51,6 +51,7 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -62,6 +63,7 @@ import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; +import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.metrics.MetricsFactory; import io.quarkus.runtime.util.HashUtil; import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem; @@ -142,7 +144,8 @@ void collectComponents(BeanDiscoveryFinishedBuildItem beanDiscoveryFinished, BuildProducer mediatorMethods, BuildProducer emitters, BuildProducer channels, - BuildProducer validationErrors) { + BuildProducer validationErrors, + BuildProducer configDescriptionBuildItemBuildProducer) { // We need to collect all business methods annotated with @Incoming/@Outgoing first for (BeanInfo bean : beanDiscoveryFinished.beanStream().classBeans()) { @@ -162,10 +165,20 @@ void collectComponents(BeanDiscoveryFinishedBuildItem beanDiscoveryFinished, validationErrors.produce(new ValidationErrorBuildItem( new DeploymentException("Empty @Incoming annotation on method " + method))); } + if (incoming != null) { + configDescriptionBuildItemBuildProducer.produce(new ConfigDescriptionBuildItem( + "mp.messaging.incoming." + incoming.value().asString() + ".connector", String.class, null, + "The connector to use", null, null, ConfigPhase.BUILD_TIME)); + } if (outgoing != null && outgoing.value().asString().isEmpty()) { validationErrors.produce(new ValidationErrorBuildItem( new DeploymentException("Empty @Outgoing annotation on method " + method))); } + if (outgoing != null) { + configDescriptionBuildItemBuildProducer.produce(new ConfigDescriptionBuildItem( + "mp.messaging.outgoing." + outgoing.value().asString() + ".connector", String.class, null, + "The connector to use", null, null, ConfigPhase.BUILD_TIME)); + } if (isSynthetic(method.flags())) { continue; } diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java similarity index 89% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java index 92f3e7a982e26..bd48ee87f995c 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/AnnotatedWebsocketEndpointBuildItem.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import io.quarkus.builder.item.MultiBuildItem; diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerBuildItem.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerBuildItem.java similarity index 90% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerBuildItem.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerBuildItem.java index 1f0d7b1861ca6..6395347a89935 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerBuildItem.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerBuildItem.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.runtime.RuntimeValue; diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java similarity index 74% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java index 1180537fd65cd..2e4f0d7237bad 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/ServerWebSocketContainerFactoryBuildItem.java @@ -1,7 +1,7 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import io.quarkus.builder.item.SimpleBuildItem; -import io.quarkus.undertow.websockets.client.runtime.ServerWebSocketContainerFactory; +import io.quarkus.websockets.client.runtime.ServerWebSocketContainerFactory; public final class ServerWebSocketContainerFactoryBuildItem extends SimpleBuildItem { diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java similarity index 89% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java index 3bcc0d794e577..daadfba2e5c96 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebSocketDeploymentInfoBuildItem.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.runtime.RuntimeValue; diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketClientProcessor.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketClientProcessor.java similarity index 98% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketClientProcessor.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketClientProcessor.java index c38f426a850e0..0d5de63ea0358 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketClientProcessor.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketClientProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import java.lang.reflect.Modifier; import java.util.Collection; @@ -37,7 +37,7 @@ import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.runtime.RuntimeValue; import io.quarkus.undertow.deployment.ServletContextAttributeBuildItem; -import io.quarkus.undertow.websockets.client.runtime.WebsocketCoreRecorder; +import io.quarkus.websockets.client.runtime.WebsocketCoreRecorder; import io.undertow.websockets.DefaultContainerConfigurator; import io.undertow.websockets.ServerWebSocketContainer; import io.undertow.websockets.UndertowContainerProvider; diff --git a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketConfig.java b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketConfig.java similarity index 92% rename from extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketConfig.java rename to extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketConfig.java index 4b8cfc95035f8..51a5000d50698 100644 --- a/extensions/websockets/client/deployment/src/main/java/io/quarkus/undertow/websockets/client/deployment/WebsocketConfig.java +++ b/extensions/websockets/client/deployment/src/main/java/io/quarkus/websockets/client/deployment/WebsocketConfig.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.deployment; +package io.quarkus.websockets.client.deployment; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; diff --git a/extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/TestWebSocketClient.java b/extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/TestWebSocketClient.java similarity index 91% rename from extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/TestWebSocketClient.java rename to extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/TestWebSocketClient.java index 4d0c779e19e9d..83744638f7a29 100644 --- a/extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/TestWebSocketClient.java +++ b/extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/TestWebSocketClient.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.test; +package io.quarkus.websockets.test; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; diff --git a/extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebSocketClientTestCase.java b/extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/WebSocketClientTestCase.java similarity index 98% rename from extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebSocketClientTestCase.java rename to extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/WebSocketClientTestCase.java index 5db5d366406b7..19824dc551894 100644 --- a/extensions/websockets/client/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebSocketClientTestCase.java +++ b/extensions/websockets/client/deployment/src/test/java/io/quarkus/websockets/test/WebSocketClientTestCase.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.test; +package io.quarkus.websockets.test; import java.net.URI; import java.util.concurrent.CountDownLatch; diff --git a/extensions/websockets/client/runtime/pom.xml b/extensions/websockets/client/runtime/pom.xml index 25d6e7d215fe5..4e508412af5d6 100644 --- a/extensions/websockets/client/runtime/pom.xml +++ b/extensions/websockets/client/runtime/pom.xml @@ -23,6 +23,10 @@ io.quarkus quarkus-vertx + + io.quarkus.security + quarkus-security + jakarta.websocket jakarta.websocket-api diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/BearerTokenClientEndpointConfigurator.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/BearerTokenClientEndpointConfigurator.java new file mode 100644 index 0000000000000..ede94928dc32d --- /dev/null +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/BearerTokenClientEndpointConfigurator.java @@ -0,0 +1,56 @@ +package io.quarkus.websockets; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.websocket.ClientEndpointConfig; +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.Extension; + +import io.vertx.core.http.HttpHeaders; + +public class BearerTokenClientEndpointConfigurator implements ClientEndpointConfig { + + final String token; + + public BearerTokenClientEndpointConfigurator(String token) { + this.token = token; + } + + @Override + public List getPreferredSubprotocols() { + return Collections.emptyList(); + } + + @Override + public List getExtensions() { + return Collections.emptyList(); + } + + @Override + public Configurator getConfigurator() { + return new Configurator() { + @Override + public void beforeRequest(Map> headers) { + headers.put(HttpHeaders.AUTHORIZATION.toString(), Collections.singletonList("Bearer " + token)); + } + }; + } + + @Override + public List> getEncoders() { + return Collections.emptyList(); + } + + @Override + public List> getDecoders() { + return Collections.emptyList(); + } + + @Override + public Map getUserProperties() { + return Collections.emptyMap(); + } +} diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ExecutorSupplier.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ExecutorSupplier.java similarity index 82% rename from extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ExecutorSupplier.java rename to extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ExecutorSupplier.java index a4905686beb0b..afe8accd528f1 100644 --- a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ExecutorSupplier.java +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ExecutorSupplier.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.client.runtime; +package io.quarkus.websockets.client.runtime; import java.util.concurrent.Executor; import java.util.function.Supplier; diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ServerWebSocketContainerFactory.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ServerWebSocketContainerFactory.java similarity index 83% rename from extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ServerWebSocketContainerFactory.java rename to extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ServerWebSocketContainerFactory.java index 2f545ca292155..57e610501c831 100644 --- a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/ServerWebSocketContainerFactory.java +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/ServerWebSocketContainerFactory.java @@ -1,6 +1,7 @@ -package io.quarkus.undertow.websockets.client.runtime; +package io.quarkus.websockets.client.runtime; import java.net.InetSocketAddress; +import java.security.Principal; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Supplier; @@ -17,5 +18,6 @@ public interface ServerWebSocketContainerFactory { ServerWebSocketContainer create(ObjectIntrospecter objectIntrospecter, ClassLoader classLoader, Supplier eventLoopSupplier, List contextSetupHandlers, boolean dispatchToWorker, InetSocketAddress clientBindAddress, WebSocketReconnectHandler reconnectHandler, - Supplier executorSupplier, List installedExtensions, int maxFrameSize); + Supplier executorSupplier, List installedExtensions, int maxFrameSize, + Supplier currentUserSupplier); } diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebSocketPrincipal.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebSocketPrincipal.java new file mode 100644 index 0000000000000..f364c20807bce --- /dev/null +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebSocketPrincipal.java @@ -0,0 +1,24 @@ +package io.quarkus.websockets.client.runtime; + +import java.security.Principal; + +import io.quarkus.security.identity.SecurityIdentity; + +public class WebSocketPrincipal implements Principal { + + final SecurityIdentity securityIdentity; + + public WebSocketPrincipal(SecurityIdentity securityIdentity) { + this.securityIdentity = securityIdentity; + } + + @Override + public String getName() { + return securityIdentity.getPrincipal().getName(); + } + + public SecurityIdentity getSecurityIdentity() { + return securityIdentity; + } + +} diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/WebsocketCoreRecorder.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebsocketCoreRecorder.java similarity index 81% rename from extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/WebsocketCoreRecorder.java rename to extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebsocketCoreRecorder.java index 1a420478fefb4..93ed683e37a61 100644 --- a/extensions/websockets/client/runtime/src/main/java/io/quarkus/undertow/websockets/client/runtime/WebsocketCoreRecorder.java +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/WebsocketCoreRecorder.java @@ -1,11 +1,13 @@ -package io.quarkus.undertow.websockets.client.runtime; +package io.quarkus.websockets.client.runtime; +import java.security.Principal; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Supplier; +import javax.enterprise.inject.Instance; import javax.websocket.DeploymentException; import javax.websocket.Endpoint; import javax.websocket.server.ServerApplicationConfig; @@ -19,9 +21,11 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.security.identity.CurrentIdentityAssociation; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; import io.undertow.websockets.ServerWebSocketContainer; import io.undertow.websockets.UndertowContainerProvider; +import io.undertow.websockets.UndertowSession; import io.undertow.websockets.WebSocketDeploymentInfo; import io.undertow.websockets.util.ContextSetupHandler; import io.undertow.websockets.util.ObjectFactory; @@ -115,6 +119,9 @@ public RuntimeValue createServerContainer(BeanContaine if (serverContainerFactory == null) { serverContainerFactory = ServerWebSocketContainer::new; } + Instance currentIdentityAssociation = Arc.container() + .select(CurrentIdentityAssociation.class); + ServerWebSocketContainer container = serverContainerFactory.create(new ObjectIntrospecter() { @Override public ObjectFactory createInstanceFactory(Class clazz) { @@ -147,16 +154,31 @@ public EventLoopGroup get() { @Override public Action create(Action action) { return new Action() { + + CurrentIdentityAssociation getCurrentIdentityAssociation() { + if (currentIdentityAssociation.isResolvable()) { + return currentIdentityAssociation.get(); + } + return null; + } + @Override - public T call(C context) throws Exception { + public T call(C context, UndertowSession session) throws Exception { ClassLoader old = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(cl); boolean required = !requestContext.isActive(); if (required) { requestContext.activate(); + Principal p = session.getUserPrincipal(); + if (p instanceof WebSocketPrincipal) { + var current = getCurrentIdentityAssociation(); + if (current != null) { + current.setIdentity(((WebSocketPrincipal) p).getSecurityIdentity()); + } + } } try { - return action.call(context); + return action.call(context, session); } finally { try { if (required) { @@ -175,7 +197,15 @@ public T call(C context) throws Exception { null, info.getExecutor(), Collections.emptyList(), - info.getMaxFrameSize()); + info.getMaxFrameSize(), new Supplier() { + @Override + public Principal get() { + if (currentIdentityAssociation.isResolvable()) { + return new WebSocketPrincipal(currentIdentityAssociation.get().getIdentity()); + } + return null; + } + }); for (Class i : info.getAnnotatedEndpoints()) { container.addEndpoint(i); } diff --git a/extensions/websockets/server/deployment/src/main/java/io/quarkus/undertow/websockets/deployment/ServerWebSocketProcessor.java b/extensions/websockets/server/deployment/src/main/java/io/quarkus/websockets/deployment/ServerWebSocketProcessor.java similarity index 85% rename from extensions/websockets/server/deployment/src/main/java/io/quarkus/undertow/websockets/deployment/ServerWebSocketProcessor.java rename to extensions/websockets/server/deployment/src/main/java/io/quarkus/websockets/deployment/ServerWebSocketProcessor.java index 2094cb293218c..cbff174071b16 100644 --- a/extensions/websockets/server/deployment/src/main/java/io/quarkus/undertow/websockets/deployment/ServerWebSocketProcessor.java +++ b/extensions/websockets/server/deployment/src/main/java/io/quarkus/websockets/deployment/ServerWebSocketProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.deployment; +package io.quarkus.websockets.deployment; import java.lang.reflect.Modifier; import java.util.Collection; @@ -20,13 +20,13 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.undertow.websockets.client.deployment.AnnotatedWebsocketEndpointBuildItem; -import io.quarkus.undertow.websockets.client.deployment.ServerWebSocketContainerBuildItem; -import io.quarkus.undertow.websockets.client.deployment.ServerWebSocketContainerFactoryBuildItem; -import io.quarkus.undertow.websockets.client.deployment.WebSocketDeploymentInfoBuildItem; -import io.quarkus.undertow.websockets.client.deployment.WebsocketClientProcessor; -import io.quarkus.undertow.websockets.runtime.WebsocketServerRecorder; import io.quarkus.vertx.http.deployment.FilterBuildItem; +import io.quarkus.websockets.client.deployment.AnnotatedWebsocketEndpointBuildItem; +import io.quarkus.websockets.client.deployment.ServerWebSocketContainerBuildItem; +import io.quarkus.websockets.client.deployment.ServerWebSocketContainerFactoryBuildItem; +import io.quarkus.websockets.client.deployment.WebSocketDeploymentInfoBuildItem; +import io.quarkus.websockets.client.deployment.WebsocketClientProcessor; +import io.quarkus.websockets.runtime.WebsocketServerRecorder; public class ServerWebSocketProcessor { diff --git a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoService.java b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoService.java similarity index 77% rename from extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoService.java rename to extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoService.java index 4db7b3e9ad27e..e8258090f203b 100644 --- a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoService.java +++ b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoService.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.test; +package io.quarkus.websockets.test; import javax.enterprise.context.RequestScoped; diff --git a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoWebSocket.java b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoWebSocket.java similarity index 86% rename from extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoWebSocket.java rename to extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoWebSocket.java index 8af19f7613493..1f5c7a84c4a4b 100644 --- a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/EchoWebSocket.java +++ b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/EchoWebSocket.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.test; +package io.quarkus.websockets.test; import javax.inject.Inject; import javax.websocket.OnMessage; diff --git a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebsocketDevModeTestCase.java b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/WebsocketDevModeTestCase.java similarity index 98% rename from extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebsocketDevModeTestCase.java rename to extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/WebsocketDevModeTestCase.java index 82451f3262efa..7f506a93fe645 100644 --- a/extensions/websockets/server/deployment/src/test/java/io/quarkus/undertow/websockets/test/WebsocketDevModeTestCase.java +++ b/extensions/websockets/server/deployment/src/test/java/io/quarkus/websockets/test/WebsocketDevModeTestCase.java @@ -1,4 +1,4 @@ -package io.quarkus.undertow.websockets.test; +package io.quarkus.websockets.test; import java.net.URI; import java.util.concurrent.LinkedBlockingDeque; diff --git a/extensions/websockets/server/runtime/src/main/java/io/quarkus/undertow/websockets/runtime/WebsocketServerRecorder.java b/extensions/websockets/server/runtime/src/main/java/io/quarkus/undertow/websockets/runtime/WebsocketServerRecorder.java deleted file mode 100644 index 33a418dfc5f66..0000000000000 --- a/extensions/websockets/server/runtime/src/main/java/io/quarkus/undertow/websockets/runtime/WebsocketServerRecorder.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.quarkus.undertow.websockets.runtime; - -import java.net.InetSocketAddress; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -import javax.websocket.DeploymentException; -import javax.websocket.Extension; - -import org.jboss.logging.Logger; - -import io.netty.channel.EventLoopGroup; -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.undertow.websockets.client.runtime.ServerWebSocketContainerFactory; -import io.quarkus.undertow.websockets.client.runtime.WebsocketCoreRecorder; -import io.undertow.websockets.ServerWebSocketContainer; -import io.undertow.websockets.WebSocketDeploymentInfo; -import io.undertow.websockets.WebSocketReconnectHandler; -import io.undertow.websockets.util.ContextSetupHandler; -import io.undertow.websockets.util.ObjectIntrospecter; -import io.undertow.websockets.vertx.VertxServerWebSocketContainer; -import io.undertow.websockets.vertx.VertxWebSocketHandler; -import io.vertx.core.Handler; -import io.vertx.ext.web.RoutingContext; - -@Recorder -public class WebsocketServerRecorder { - - private static final Logger log = Logger.getLogger(WebsocketCoreRecorder.class); - - public Handler createHandler(RuntimeValue info, - RuntimeValue container) throws DeploymentException { - return new VertxWebSocketHandler(container.getValue(), info.getValue()); - } - - public ServerWebSocketContainerFactory createFactory() { - return new ServerWebSocketContainerFactory() { - @Override - public ServerWebSocketContainer create(ObjectIntrospecter objectIntrospecter, ClassLoader classLoader, - Supplier eventLoopSupplier, List contextSetupHandlers, - boolean dispatchToWorker, InetSocketAddress clientBindAddress, WebSocketReconnectHandler reconnectHandler, - Supplier executorSupplier, List installedExtensions, int maxFrameSize) { - return new VertxServerWebSocketContainer(objectIntrospecter, classLoader, eventLoopSupplier, - contextSetupHandlers, dispatchToWorker, clientBindAddress, reconnectHandler, executorSupplier, - installedExtensions, maxFrameSize); - } - }; - } -} diff --git a/extensions/websockets/server/runtime/src/main/java/io/quarkus/websockets/runtime/WebsocketServerRecorder.java b/extensions/websockets/server/runtime/src/main/java/io/quarkus/websockets/runtime/WebsocketServerRecorder.java new file mode 100644 index 0000000000000..7490203995cdf --- /dev/null +++ b/extensions/websockets/server/runtime/src/main/java/io/quarkus/websockets/runtime/WebsocketServerRecorder.java @@ -0,0 +1,104 @@ +package io.quarkus.websockets.runtime; + +import java.net.InetSocketAddress; +import java.security.Principal; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import javax.websocket.DeploymentException; +import javax.websocket.Extension; + +import org.jboss.logging.Logger; + +import io.netty.channel.EventLoopGroup; +import io.quarkus.arc.Arc; +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.security.identity.CurrentIdentityAssociation; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; +import io.quarkus.websockets.client.runtime.ServerWebSocketContainerFactory; +import io.quarkus.websockets.client.runtime.WebSocketPrincipal; +import io.quarkus.websockets.client.runtime.WebsocketCoreRecorder; +import io.undertow.websockets.ServerWebSocketContainer; +import io.undertow.websockets.WebSocketDeploymentInfo; +import io.undertow.websockets.WebSocketReconnectHandler; +import io.undertow.websockets.util.ContextSetupHandler; +import io.undertow.websockets.util.ObjectIntrospecter; +import io.undertow.websockets.vertx.VertxServerWebSocketContainer; +import io.undertow.websockets.vertx.VertxWebSocketHandler; +import io.undertow.websockets.vertx.VertxWebSocketHttpExchange; +import io.vertx.core.Handler; +import io.vertx.core.net.impl.ConnectionBase; +import io.vertx.ext.web.RoutingContext; + +@Recorder +public class WebsocketServerRecorder { + + private static final Logger log = Logger.getLogger(WebsocketCoreRecorder.class); + + public Handler createHandler(RuntimeValue info, + RuntimeValue container) throws DeploymentException { + return new VertxWebSocketHandler(container.getValue(), info.getValue()) { + @Override + protected VertxWebSocketHttpExchange createHttpExchange(RoutingContext event) { + return new QuarkusVertxWebSocketHttpExchange(executor, event); + } + }; + } + + public ServerWebSocketContainerFactory createFactory() { + return new ServerWebSocketContainerFactory() { + @Override + public ServerWebSocketContainer create(ObjectIntrospecter objectIntrospecter, ClassLoader classLoader, + Supplier eventLoopSupplier, List contextSetupHandlers, + boolean dispatchToWorker, InetSocketAddress clientBindAddress, WebSocketReconnectHandler reconnectHandler, + Supplier executorSupplier, List installedExtensions, int maxFrameSize, + Supplier currentUserSupplier) { + var identity = Arc.container().instance(CurrentIdentityAssociation.class); + return new VertxServerWebSocketContainer(objectIntrospecter, classLoader, eventLoopSupplier, + contextSetupHandlers, dispatchToWorker, clientBindAddress, reconnectHandler, executorSupplier, + installedExtensions, maxFrameSize, currentUserSupplier) { + @Override + protected VertxWebSocketHttpExchange createHttpExchange(RoutingContext routingContext) { + QuarkusHttpUser user = (QuarkusHttpUser) routingContext.user(); + if (user != null) { + //close the connection when the identity expires + Long expire = user.getSecurityIdentity().getAttribute("quarkus.identity.expire-time"); + if (expire != null) { + ((ConnectionBase) routingContext.request().connection()).channel().eventLoop() + .schedule(new Runnable() { + @Override + public void run() { + routingContext.request().connection().close(); + } + }, expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + } + return new QuarkusVertxWebSocketHttpExchange(getExecutorSupplier().get(), routingContext); + } + }; + } + }; + } + + private static class QuarkusVertxWebSocketHttpExchange extends VertxWebSocketHttpExchange { + + private final RoutingContext routingContext; + + public QuarkusVertxWebSocketHttpExchange(Executor executor, RoutingContext routingContext) { + super(executor, routingContext); + this.routingContext = routingContext; + } + + @Override + public Principal getUserPrincipal() { + QuarkusHttpUser user = (QuarkusHttpUser) routingContext.user(); + if (user != null) { + return new WebSocketPrincipal(user.getSecurityIdentity()); + } + return null; + } + } +} diff --git a/integration-tests/oidc/pom.xml b/integration-tests/oidc/pom.xml index 37a87f7a9bbcd..e4259d8420468 100644 --- a/integration-tests/oidc/pom.xml +++ b/integration-tests/oidc/pom.xml @@ -22,6 +22,10 @@ io.quarkus quarkus-resteasy-reactive-jackson + + io.quarkus + quarkus-websockets + io.quarkus @@ -84,6 +88,19 @@ + + io.quarkus + quarkus-websockets-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/SecuredHelloWebSocket.java b/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/SecuredHelloWebSocket.java new file mode 100644 index 0000000000000..cd6d270ca5ee9 --- /dev/null +++ b/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/SecuredHelloWebSocket.java @@ -0,0 +1,23 @@ +package io.quarkus.it.keycloak; + +import javax.annotation.security.RolesAllowed; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.websocket.OnMessage; +import javax.websocket.server.ServerEndpoint; + +import io.quarkus.security.identity.SecurityIdentity; + +@ServerEndpoint("/secured-hello") +@ApplicationScoped +@RolesAllowed("user") +public class SecuredHelloWebSocket { + + @Inject + SecurityIdentity identity; + + @OnMessage + public String onMessage(String message) { + return message + " " + identity.getPrincipal().getName(); + } +} diff --git a/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/WebsocketOidcTestCase.java b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/WebsocketOidcTestCase.java new file mode 100644 index 0000000000000..a8397e06db357 --- /dev/null +++ b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/WebsocketOidcTestCase.java @@ -0,0 +1,55 @@ +package io.quarkus.it.keycloak; + +import static io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager.getAccessToken; + +import java.net.URI; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ContainerProvider; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager; +import io.quarkus.websockets.BearerTokenClientEndpointConfigurator; + +@QuarkusTest +@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class) +public class WebsocketOidcTestCase { + + @TestHTTPResource("secured-hello") + URI wsUri; + + @Test + public void websocketTest() throws Exception { + + LinkedBlockingDeque message = new LinkedBlockingDeque<>(); + Session session = ContainerProvider.getWebSocketContainer().connectToServer(new Endpoint() { + @Override + public void onOpen(Session session, EndpointConfig endpointConfig) { + session.addMessageHandler(new MessageHandler.Whole() { + @Override + public void onMessage(String s) { + message.add(s); + } + }); + session.getAsyncRemote().sendText("hello"); + } + }, new BearerTokenClientEndpointConfigurator(getAccessToken("alice")), wsUri); + + try { + Assertions.assertEquals("hello alice@gmail.com", message.poll(20, TimeUnit.SECONDS)); + } finally { + session.close(); + } + } + +}