Skip to content

Commit

Permalink
Merge pull request #41212 from gsmet/3.12.0-backports-1
Browse files Browse the repository at this point in the history
[3.12] 3.12.0 backports 1
  • Loading branch information
gsmet authored Jun 15, 2024
2 parents f470ffb + f3a1a51 commit cd07a9e
Show file tree
Hide file tree
Showing 30 changed files with 788 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ private ContainerRuntimeUtil() {
* @return a fully resolved {@link ContainerRuntime} indicating if Docker or Podman is available and in rootless mode or not
* @throws IllegalStateException if no container runtime was found to build the image
*/
public static ContainerRuntime detectContainerRuntime() {
return detectContainerRuntime(true);
}

public static ContainerRuntime detectContainerRuntime(ContainerRuntime... orderToCheckRuntimes) {
return detectContainerRuntime(true, orderToCheckRuntimes);
}
Expand Down
37 changes: 37 additions & 0 deletions docs/src/main/asciidoc/security-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,43 @@ If it becomes necessary to test security features using both `@TestSecurity` and
mechanism when none is defined), then Basic Auth needs to be enabled explicitly,
for example by setting `quarkus.http.auth.basic=true` or `%test.quarkus.http.auth.basic=true`.

Similarly, if it becomes necessary to test security features using both `@TestSecurity` and Bearer token authentication,
you can leverage both like in the example below:

[source, java]
----
@Test
@TestSecurity(user = "Bob")
public void testSecurityMetaAnnotation {
RestAssured.given()
.auth().oauth2(getTokenForUser("Alice")) <1>
.get("hello")
.then()
.statusCode(200)
.body(Matchers.is("Hello Alice"));
RestAssured.given()
.get("hello")
.then()
.statusCode(200)
.body(Matchers.is("Hello Bob")); <2>
}
@Path("hello")
public static class HelloResource {
@Inject
SecurityIdentity identity;
@Authenticated
@GET
public String sayHello() {
return "Hello " + identity.getPrincipal().getName();
}
}
----
<1> Bearer token authentication mechanism is used, because a Bearer access token is sent with the HTTP request.
<2> No authorization header is present, therefore the Test Security Extension creates user `Bob`.

=== Path-based authentication

`@TestSecurity` can also be used when xref:security-authentication-mechanisms.adoc#combining-authentication-mechanisms[authentication mechanisms must be combined].
Expand Down
31 changes: 27 additions & 4 deletions docs/src/main/asciidoc/websockets-next-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -523,18 +523,18 @@ For example, `OpenConnections#findByEndpointId(String)` makes it easy to find co

=== CDI events

Quarkus fires a CDI event of type `io.quarkus.websockets.next.WebSocketConnection` with qualifier `@io.quarkus.websockets.next.ConnectionOpen` asynchronously when a new connection is opened.
Moreover, a CDI event of type `WebSocketConnection` with qualifier `@io.quarkus.websockets.next.ConnectionClosed` is fired asynchronously when a connection is closed.
Quarkus fires a CDI event of type `io.quarkus.websockets.next.WebSocketConnection` with qualifier `@io.quarkus.websockets.next.Open` asynchronously when a new connection is opened.
Moreover, a CDI event of type `WebSocketConnection` with qualifier `@io.quarkus.websockets.next.Closed` is fired asynchronously when a connection is closed.

[source, java]
----
import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.ConnectionOpen;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketConnection;
class MyBean {
void connectionOpened(@ObservesAsync @ConnectionOpen WebSocketConnection connection) { <1>
void connectionOpened(@ObservesAsync @Open WebSocketConnection connection) { <1>
// This observer method is called when a connection is opened...
}
}
Expand Down Expand Up @@ -765,6 +765,29 @@ public class ExampleHttpUpgradeCheck implements HttpUpgradeCheck {

TIP: You can choose WebSocket endpoints to which the `HttpUpgradeCheck` is applied with the `HttpUpgradeCheck#appliesTo` method.

[[traffic-logging]]
== Traffic logging

Quarkus can log the messages sent and received for debugging purposes.
To enable traffic logging, set the `quarkus.websockets-next.server.traffic-logging.enabled` configuration property to `true`.
The payload of text messages is logged as well.
However, the number of logged characters is limited.
The default limit is 100, but you can change this limit with the `quarkus.websockets-next.server.traffic-logging.text-payload-limit` configuration property.

TIP: The messages are only logged if the `DEBUG` level is enabled for the logger `io.quarkus.websockets.next.traffic`.

.Example configuration
[source, properties]
----
quarkus.websockets-next.server.traffic-logging.enabled=true <1>
quarkus.websockets-next.server.traffic-logging.text-payload-limit=50 <2>
quarkus.log.category."io.quarkus.websockets.next.traffic".level=DEBUG <3>
----
<1> Enables traffic logging.
<2> Set the number of characters of a text message payload which will be logged.
<3> Enable `DEBUG` level is for the logger `io.quarkus.websockets.next.traffic`.

[[websocket-next-configuration-reference]]
== Configuration reference

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.quarkus.resteasy.reactive.server.test.preconditions;

import static io.restassured.RestAssured.get;

import java.time.Instant;
import java.util.Date;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Request;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class DatePreconditionTests {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Resource.class));

// Make sure we test a subtype of Date, since that is what Hibernate ORM gives us most of the time (hah)
// Also make sure we have non-zero milliseconds, since that will be the case for most date values representing
// "now", and we want to make sure pre-conditions work (second-resolution)
static final Date date = new Date(Date.from(Instant.parse("2007-12-03T10:15:30.24Z")).getTime()) {
};

public static class Something {
}

@Test
public void test() {
get("/preconditions")
.then()
.statusCode(200)
.header("Last-Modified", "Mon, 03 Dec 2007 10:15:30 GMT")
.body(Matchers.equalTo("foo"));
RestAssured
.with()
.header("If-Modified-Since", "Mon, 03 Dec 2007 10:15:30 GMT")
.get("/preconditions")
.then()
.statusCode(304);
}

@Path("/preconditions")
public static class Resource {
@GET
public Response get(Request request) {
ResponseBuilder resp = request.evaluatePreconditions(date);
if (resp != null) {
return resp.build();
}
return Response.ok("foo").lastModified(date).build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.quarkus.websockets.next.test.traffic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.websockets.next.BasicWebSocketConnector;
import io.quarkus.websockets.next.WebSocketClientConnection;
import io.vertx.core.Context;

public class BasicConnectorTrafficLoggerTest extends TrafficLoggerTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot(root -> {
root.addClasses(Endpoint.class);
TrafficLoggerTest.addApplicationProperties(root, false);
})
.setLogRecordPredicate(TrafficLoggerTest::isTrafficLogRecord)
.assertLogRecords(logRecordsConsumer(true));

@Inject
BasicWebSocketConnector connector;

@Test
public void testTrafficLogger() throws InterruptedException {
List<String> messages = new CopyOnWriteArrayList<>();
CountDownLatch closedLatch = new CountDownLatch(1);
CountDownLatch messageLatch = new CountDownLatch(1);
WebSocketClientConnection conn = connector
.baseUri(endUri)
.path("end")
.onTextMessage((c, m) -> {
assertTrue(Context.isOnWorkerThread());
messages.add(m);
messageLatch.countDown();
})
.onClose((c, s) -> closedLatch.countDown())
.connectAndAwait();
conn.sendTextAndAwait("dummy");
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
assertEquals("ok", messages.get(0));
conn.closeAndAwait();
assertTrue(closedLatch.await(5, TimeUnit.SECONDS));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.quarkus.websockets.next.test.traffic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.websockets.next.OnClose;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocketClient;
import io.quarkus.websockets.next.WebSocketClientConnection;
import io.quarkus.websockets.next.WebSocketConnector;

public class ClientEndpointTrafficLoggerTest extends TrafficLoggerTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot(root -> {
root.addClasses(Endpoint.class, Client.class);
TrafficLoggerTest.addApplicationProperties(root, false);
})
.setLogRecordPredicate(
TrafficLoggerTest::isTrafficLogRecord)
.assertLogRecords(logRecordsConsumer(true));

@Inject
WebSocketConnector<Client> connector;

@Test
public void testTrafficLogger() throws InterruptedException {
WebSocketClientConnection conn = connector
.baseUri(endUri)
.connectAndAwait();
assertTrue(Client.MESSAGE_LATCH.await(5, TimeUnit.SECONDS));
assertEquals("ok", Client.MESSAGES.get(0));
conn.closeAndAwait();
assertTrue(Client.CLOSED_LATCH.await(5, TimeUnit.SECONDS));
assertTrue(Endpoint.CLOSED_LATCH.await(5, TimeUnit.SECONDS));
}

@WebSocketClient(path = "/end")
public static class Client {

static final List<String> MESSAGES = new CopyOnWriteArrayList<>();

static final CountDownLatch MESSAGE_LATCH = new CountDownLatch(1);

static final CountDownLatch CLOSED_LATCH = new CountDownLatch(1);

@OnTextMessage
void onMessage(String message) {
MESSAGES.add(message);
MESSAGE_LATCH.countDown();
}

@OnClose
void onClose() {
CLOSED_LATCH.countDown();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.quarkus.websockets.next.test.traffic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.websockets.next.test.utils.WSClient;
import io.vertx.core.Vertx;

public class ServerTrafficLoggerTest extends TrafficLoggerTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot(root -> {
root.addClasses(Endpoint.class, WSClient.class);
TrafficLoggerTest.addApplicationProperties(root, true);
})
.setLogRecordPredicate(TrafficLoggerTest::isTrafficLogRecord)
.assertLogRecords(logRecordsConsumer(false));

@Inject
Vertx vertx;

@Test
public void testTrafficLogger() throws InterruptedException, ExecutionException {
try (WSClient client = new WSClient(vertx)) {
client.connect(WSClient.toWS(endUri, "end"));
client.waitForMessages(1);
assertEquals("ok", client.getMessages().get(0).toString());
}
assertTrue(Endpoint.CLOSED_LATCH.await(5, TimeUnit.SECONDS));
}

}
Loading

0 comments on commit cd07a9e

Please sign in to comment.