Skip to content

Commit

Permalink
Merge branch 'main' into podman
Browse files Browse the repository at this point in the history
  • Loading branch information
n1hility authored Mar 27, 2024
2 parents c32df29 + cc7a312 commit c970d30
Show file tree
Hide file tree
Showing 48 changed files with 1,147 additions and 176 deletions.
4 changes: 2 additions & 2 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
<jakarta.ws.rs-api.version>3.1.0</jakarta.ws.rs-api.version>
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
<jaxb-runtime.version>4.0.5</jaxb-runtime.version>
<asm.version>9.6</asm.version>
<asm.version>9.7</asm.version>
<commons-io.version>2.15.1</commons-io.version>
<jboss-metadata-web.version>16.0.0.Final</jboss-metadata-web.version>
<maven-toolchain.version>3.0-alpha-2</maven-toolchain.version>
Expand Down Expand Up @@ -223,7 +223,7 @@
<java-buildpack-client.version>0.0.6</java-buildpack-client.version>
<org-crac.version>0.1.3</org-crac.version>
<sshd-common.version>2.12.0</sshd-common.version>
<mime4j.version>0.8.9</mime4j.version>
<mime4j.version>0.8.11</mime4j.version>
<mutiny-zero.version>1.0.0</mutiny-zero.version>
<pulsar-client.version>3.0.0</pulsar-client.version>
<async-http-client.version>2.12.3</async-http-client.version>
Expand Down
2 changes: 1 addition & 1 deletion bom/dev-ui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<description>Dependency management for dev-ui. Importable by third party extension developers.</description>

<properties>
<vaadin.version>24.3.8</vaadin.version>
<vaadin.version>24.3.10</vaadin.version>
<lit.version>3.1.2</lit.version>
<lit-element.version>4.0.4</lit-element.version>
<lit-html.version>3.1.2</lit-html.version>
Expand Down
2 changes: 1 addition & 1 deletion build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
<wiremock-maven-plugin.version>7.3.0</wiremock-maven-plugin.version>

<!-- Artemis test dependencies -->
<artemis.version>2.32.0</artemis.version>
<artemis.version>2.33.0</artemis.version>

<!-- Code Coverage Properties-->
<jacoco.agent.argLine></jacoco.agent.argLine>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.jboss.logging.Logger;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.pkg.NativeConfig;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
Expand All @@ -34,6 +35,7 @@ public class UpxCompressionBuildStep {
*/
private static final String PATH = "PATH";

@BuildStep(onlyIf = NativeBuild.class)
public void compress(NativeConfig nativeConfig, NativeImageRunnerBuildItem nativeImageRunner,
NativeImageBuildItem image,
BuildProducer<UpxCompressedBuildItem> upxCompressedProducer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew
args.add("-PquarkusPluginVersion=" + ToolsUtils.getGradlePluginVersion(props));
args.add("--console");
args.add("plain");
args.add("--no-daemon");
args.add("--stacktrace");
args.add("quarkusUpdate");
if (!StringUtil.isNullOrEmpty(targetQuarkusVersion.platformVersion)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,15 @@ protected Properties getBuildSystemProperties(boolean quarkusOnly) throws MojoEx
* @return true if the package type system property was set, otherwise - false
*/
protected boolean setNativeEnabledIfNativeProfileEnabled() {
if (isNativeProfileEnabled(mavenProject())) {
System.setProperty("quarkus.native.enabled", "true");
if (!System.getProperties().containsKey("quarkus.native.enabled") && isNativeProfileEnabled(mavenProject())) {
Object nativeEnabledProp = mavenProject().getProperties().get("quarkus.native.enabled");
String nativeEnabled;
if (nativeEnabledProp != null) {
nativeEnabled = nativeEnabledProp.toString();
} else {
nativeEnabled = "true";
}
System.setProperty("quarkus.native.enabled", nativeEnabled);
return true;
} else {
return false;
Expand Down
264 changes: 214 additions & 50 deletions docs/src/main/asciidoc/security-openid-connect-client.adoc

Large diffs are not rendered by default.

105 changes: 71 additions & 34 deletions docs/src/main/asciidoc/security-webauthn.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ include::{includes}/extension-status.adoc[]
== Prerequisites

include::{includes}/prerequisites.adoc[]
* A WebAuthn or PassKeys-capable device, or https://developer.chrome.com/docs/devtools/webauthn/[an emulator of those].

== Introduction to WebAuthn

Expand Down Expand Up @@ -62,6 +63,14 @@ login or registration.

And also there are a lot more fields to store than just a public key, but we will help you with that.

Just in case you get there wondering what's the relation with https://fidoalliance.org/passkeys/[PassKeys]
and whether we support it: sure, yes, PassKeys is a way that your authenticator devices can share and sync
their credentials, which you can then use with our WebAuthn authentication.

NOTE: The WebAuthn specification requires `https` to be used for communication with the server, though
some browsers allow `localhost`. If you must use `https` in `DEV` mode, you can always use the
https://docs.quarkiverse.io/quarkus-ngrok/dev/index.html[quarkus-ngrok] extension.

== Architecture

In this example, we build a very simple microservice which offers four endpoints:
Expand Down Expand Up @@ -544,6 +553,7 @@ in `src/main/resources/META-INF/resources/index.html`:
<li><a href="/api/users/me">User API</a></li>
<li><a href="/api/admin">Admin API</a></li>
<li><a href="/q/webauthn/logout">Logout</a></li>
</ul>
</nav>
<div class="container">
<div class="item">
Expand Down Expand Up @@ -582,7 +592,7 @@ in `src/main/resources/META-INF/resources/index.html`:
const loginButton = document.getElementById('login');
loginButton.onclick = () => {
loginButton.addEventListener("click", (e) => {
var userName = document.getElementById('userNameLogin').value;
result.replaceChildren();
webAuthn.login({ name: userName })
Expand All @@ -593,11 +603,11 @@ in `src/main/resources/META-INF/resources/index.html`:
result.append("Login failed: "+err);
});
return false;
};
});
const registerButton = document.getElementById('register');
registerButton.onclick = () => {
registerButton.addEventListener("click", (e) => {
var userName = document.getElementById('userNameRegister').value;
var firstName = document.getElementById('firstName').value;
var lastName = document.getElementById('lastName').value;
Expand All @@ -610,7 +620,7 @@ in `src/main/resources/META-INF/resources/index.html`:
result.append("Registration failed: "+err);
});
return false;
};
});
</script>
</body>
</html>
Expand Down Expand Up @@ -639,7 +649,8 @@ form on the right, then pressing the `Register` button:

image::webauthn-2.png[role="thumb"]

Your browser will ask you to activate your WebAuthn authenticator:
Your browser will ask you to activate your WebAuthn authenticator (you will need a WebAuthn-capable browser
and possibly device, or you can use https://developer.chrome.com/docs/devtools/webauthn/[an emulator of those]):

image::webauthn-3.png[role="thumb"]

Expand Down Expand Up @@ -669,11 +680,14 @@ The Quarkus WebAuthn extension comes out of the box with these REST endpoints pr
.Request
----
{
"name": "userName",
"displayName": "Mr Nice Guy"
"name": "userName", <1>
"displayName": "Mr Nice Guy" <2>
}
----

<1> Required
<2> Optional

[source,json]
.Response
----
Expand Down Expand Up @@ -709,6 +723,26 @@ The Quarkus WebAuthn extension comes out of the box with these REST endpoints pr
}
----

=== Trigger a registration

`POST /q/webauthn/callback`: Trigger a registration

[source,json]
.Request
----
{
"id": "boMwU-QwZ_RsToPTG3iC50g8-yiKbLc3A53tgWMhzbNEQAJIlbWgchmwbt5m0ssqQNR0IM_WxCmcfKWlEao7Fg",
"rawId": "boMwU-QwZ_RsToPTG3iC50g8-yiKbLc3A53tgWMhzbNEQAJIlbWgchmwbt5m0ssqQNR0IM_WxCmcfKWlEao7Fg",
"response": {
"attestationObject": "<DATA>",
"clientDataJSON":"<DATA>"
},
"type": "public-key"
}
----

This returns a 204 with no body.

=== Obtain a login challenge

`POST /q/webauthn/login`: Set up and obtain a login challenge
Expand All @@ -717,10 +751,12 @@ The Quarkus WebAuthn extension comes out of the box with these REST endpoints pr
.Request
----
{
"name": "userName"
"name": "userName" <1>
}
----

<1> Required

[source,json]
.Response
----
Expand All @@ -746,26 +782,6 @@ The Quarkus WebAuthn extension comes out of the box with these REST endpoints pr
}
----

=== Trigger a registration

`POST /q/webauthn/callback`: Trigger a registration

[source,json]
.Request
----
{
"id": "boMwU-QwZ_RsToPTG3iC50g8-yiKbLc3A53tgWMhzbNEQAJIlbWgchmwbt5m0ssqQNR0IM_WxCmcfKWlEao7Fg",
"rawId": "boMwU-QwZ_RsToPTG3iC50g8-yiKbLc3A53tgWMhzbNEQAJIlbWgchmwbt5m0ssqQNR0IM_WxCmcfKWlEao7Fg",
"response": {
"attestationObject": "<DATA>",
"clientDataJSON":"<DATA>"
},
"type": "public-key"
}
----

This returns a 204 with no body.

=== Trigger a login

`POST /q/webauthn/callback`: Trigger a login
Expand Down Expand Up @@ -905,7 +921,13 @@ to the server to your custom login or registration endpoints.

If you are storing them in form input elements, you can then use the `WebAuthnLoginResponse` and
`WebAuthnRegistrationResponse` classes, mark them as `@BeanParam` and then use the `WebAuthnSecurity.login`
and `WebAuthnSecurity.register` methods. For example, here's how you can handle a custom login and register:
and `WebAuthnSecurity.register` methods to replace the `/q/webauthn/callback` endpoint. This even
allows you to create two separate endpoints for handling login and registration at different endpoints.

In most cases you can keep using the `/q/webauthn/login` and `/q/webauthn/register` challenge-initiating
endpoints, because this is not where custom logic is required.

For example, here's how you can handle a custom login and register action:

[source,java]
----
Expand Down Expand Up @@ -933,6 +955,7 @@ public class LoginResource {
@Inject
WebAuthnSecurity webAuthnSecurity;
// Provide an alternative implementation of the /q/webauthn/callback endpoint, only for login
@Path("/login")
@POST
@Transactional
Expand Down Expand Up @@ -962,12 +985,13 @@ public class LoginResource {
}
}
// Provide an alternative implementation of the /q/webauthn/callback endpoint, only for registration
@Path("/register")
@POST
@Transactional
public Response register(@RestForm String userName,
@BeanParam WebAuthnRegisterResponse webAuthnResponse,
RoutingContext ctx) {
@BeanParam WebAuthnRegisterResponse webAuthnResponse,
RoutingContext ctx) {
// Input validation
if(userName == null || userName.isEmpty() || !webAuthnResponse.isSet() || !webAuthnResponse.isValid()) {
return Response.status(Status.BAD_REQUEST).build();
Expand Down Expand Up @@ -1012,6 +1036,15 @@ data access with your `WebAuthnUserProvider`.
You will have to add the `@Blocking` annotation on your `WebAuthnUserProvider` class in order to tell the
Quarkus WebAuthn endpoints to defer those calls to the worker pool.

== Virtual-Threads version

If you're using a blocking data access to the database, you can safely block on the `WebAuthnSecurity` methods,
with `.await().indefinitely()`, because nothing is async in the `register` and `login` methods, besides the
data access with your `WebAuthnUserProvider`.

You will have to add the `@RunOnVirtualThread` annotation on your `WebAuthnUserProvider` class in order to tell the
Quarkus WebAuthn endpoints to defer those calls to virtual threads.

== Testing WebAuthn

Testing WebAuthn can be complicated because normally you need a hardware token, which is why we've made the
Expand Down Expand Up @@ -1139,9 +1172,9 @@ public class WebAuthnResourceTest {
.then()
.statusCode(200)
.log().ifValidationFails()
.cookie(WebAuthnController.CHALLENGE_COOKIE, Matchers.is(""))
.cookie(WebAuthnController.USERNAME_COOKIE, Matchers.is(""))
.cookie("quarkus-credential", Matchers.notNullValue());
.cookie(WebAuthnEndpointHelper.getChallengeCookie(), Matchers.is(""))
.cookie(WebAuthnEndpointHelper.getChallengeUsernameCookie(), Matchers.is(""))
.cookie(WebAuthnEndpointHelper.getMainCookie(), Matchers.notNullValue());
}
private void verifyLoggedIn(Filter cookieFilter, String userName, User user) {
Expand Down Expand Up @@ -1258,6 +1291,10 @@ public class TestUserProvider extends MyWebAuthnSetup {
[[configuration-reference]]
== Configuration Reference

The security encryption key can be set with the
link:all-config#quarkus-vertx-http_quarkus.http.auth.session.encryption-key[`quarkus.http.auth.session.encryption-key`]
configuration option, as described in the link:security-authentication-mechanisms#form-auth[security guide].

include::{generated-dir}/config/quarkus-security-webauthn.adoc[opts=optional, leveloffset=+1]

== References
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

import org.jboss.logging.Logger;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.datasource.common.runtime.DataSourceUtil;
Expand Down Expand Up @@ -99,16 +100,21 @@ public QuarkusPostgreSQLContainer(Optional<String> imageName, OptionalInt fixedE
super(DockerImageName
.parse(imageName.orElseGet(() -> ConfigureUtil.getDefaultImageNameFor("postgresql")))
.asCompatibleSubstituteFor(DockerImageName.parse(PostgreSQLContainer.IMAGE)));

this.fixedExposedPort = fixedExposedPort;
this.useSharedNetwork = useSharedNetwork;

// Workaround for https://github.com/testcontainers/testcontainers-java/issues/4799.
// The motivation of this custom wait strategy is that Testcontainers fails to start a Postgresql database when it
// has been already initialized.
// This custom wait strategy will work fine regardless of the state of the Postgresql database.
// More information in the issue ticket in Testcontainers.
this.waitStrategy = new LogMessageWaitStrategy()
.withRegEx("(" + READY_REGEX + ")?(" + SKIPPING_INITIALIZATION_REGEX + ")?")
.withTimes(2)

// Added Wait.forListeningPort() for https://github.com/quarkusio/quarkus/issues/25682
// as suggested by https://github.com/testcontainers/testcontainers-java/pull/6309
this.waitStrategy = new WaitAllStrategy()
.withStrategy(Wait.forLogMessage("(" + READY_REGEX + ")?(" + SKIPPING_INITIALIZATION_REGEX + ")?", 2))
.withStrategy(Wait.forListeningPort())
.withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS));
}

Expand Down
Loading

0 comments on commit c970d30

Please sign in to comment.