From 65f47c02af1e41cb2f5ddba24f30822998b88ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Mon, 19 Feb 2024 20:33:52 +0100 Subject: [PATCH] Refactor HttpAuthenticationMechanism relation with IdentityProvider --- .../main/asciidoc/security-customization.adoc | 6 ++++ ...abledProactiveSecIdentityProviderTest.java | 1 - .../security/HttpAuthenticationMechanism.java | 14 +++++++-- .../runtime/security/HttpAuthenticator.java | 30 ++++++++++++++++--- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index a60c30a0ca2e0..f8d3de551d00d 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -72,6 +72,12 @@ public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism } ---- +TIP: The `HttpAuthenticationMechanism` should transform incoming HTTP request with suitable authentication credentials +into an `io.quarkus.security.identity.request.AuthenticationRequest` instance and delegate the authentication to the `io.quarkus.security.identity.IdentityProviderManager`. +Leaving authentication to the `io.quarkus.security.identity.IdentityProvider`s gives you more options for credentials verifications, +as well as convenient way to perform blocking tasks. +Nevertheless, the `io.quarkus.security.identity.IdentityProvider` can be omitted and the `HttpAuthenticationMechanism` is free to authenticate request on its own in trivial use cases. + [[dealing-with-more-than-one-http-auth-mechanisms]] == Dealing with more than one HttpAuthenticationMechanism diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/DisabledProactiveSecIdentityProviderTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/DisabledProactiveSecIdentityProviderTest.java index 11bcd75daa8c8..73b1371bd20ea 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/DisabledProactiveSecIdentityProviderTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/DisabledProactiveSecIdentityProviderTest.java @@ -28,7 +28,6 @@ public class DisabledProactiveSecIdentityProviderTest { private static final String APP_PROPS = "" + - "quarkus.http.auth.basic=true\n" + "quarkus.http.auth.proactive=false\n" + "quarkus.http.auth.policy.r1.roles-allowed=admin\n" + "quarkus.http.auth.permission.roles1.paths=/admin\n" + diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java index f1996ea71553d..18598de37f640 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java @@ -3,6 +3,7 @@ import java.util.Set; import java.util.function.Function; +import io.quarkus.security.identity.IdentityProvider; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.AuthenticationRequest; @@ -21,10 +22,17 @@ public interface HttpAuthenticationMechanism { Uni getChallenge(RoutingContext context); /** - * Returns the required credential types. If there are no identity managers installed that support the - * listed types then this mechanism will not be enabled. + * If this mechanism delegates authentication to the {@link IdentityProviderManager} using the + * {@link IdentityProviderManager#authenticate(AuthenticationRequest)} call, then the mechanism must provide + * supported {@link AuthenticationRequest} request types. It allows Quarkus to validate that one or more + * {@link IdentityProvider} providers with matching supported {@link IdentityProvider#getRequestType()} request + * types exist and fail otherwise. + * + * @return required credential types */ - Set> getCredentialTypes(); + default Set> getCredentialTypes() { + return Set.of(); + } default Uni sendChallenge(RoutingContext context) { return getChallenge(context).map(new ChallengeSender(context)); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java index c7f6e9aa69af4..d624696157fbe 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java @@ -30,6 +30,7 @@ import io.quarkus.security.spi.runtime.AuthenticationFailureEvent; import io.quarkus.security.spi.runtime.AuthenticationSuccessEvent; import io.quarkus.security.spi.runtime.SecurityEventHelper; +import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @@ -47,7 +48,7 @@ public class HttpAuthenticator { public HttpAuthenticator(IdentityProviderManager identityProviderManager, Event authFailureEvent, Event authSuccessEvent, - BeanManager beanManager, + BeanManager beanManager, HttpBuildTimeConfig httpBuildTimeConfig, Instance httpAuthenticationMechanism, Instance> providers, @ConfigProperty(name = "quarkus.security.events.enabled") boolean securityEventsEnabled) { @@ -56,6 +57,15 @@ public HttpAuthenticator(IdentityProviderManager identityProviderManager, this.identityProviderManager = identityProviderManager; List mechanisms = new ArrayList<>(); for (HttpAuthenticationMechanism mechanism : httpAuthenticationMechanism) { + if (mechanism.getCredentialTypes().isEmpty()) { + // mechanism does not require any IdentityProvider + log.debugf("HttpAuthenticationMechanism '%s' provided no required credential types, therefore it needs " + + "to be able to perform authentication without any IdentityProvider", mechanism.getClass().getName()); + mechanisms.add(mechanism); + continue; + } + + // mechanism requires an IdentityProvider, therefore we verify that such a provider exists boolean found = false; for (Class mechType : mechanism.getCredentialTypes()) { for (IdentityProvider i : providers) { @@ -68,10 +78,22 @@ public HttpAuthenticator(IdentityProviderManager identityProviderManager, break; } } - // Add mechanism if there is a provider with matching credential type - // If the mechanism has no credential types, just add it anyway - if (found || mechanism.getCredentialTypes().isEmpty()) { + if (found) { mechanisms.add(mechanism); + } else if (BasicAuthenticationMechanism.class.equals(mechanism.getClass()) + && httpBuildTimeConfig.auth.basic.isEmpty()) { + log.debug(""" + BasicAuthenticationMechanism has been enabled because no other authentication mechanism has been + detected, but there is no IdentityProvider based on username and password. Please use + one of supported extensions if you plan to use the mechanism. + For more information go to the https://quarkus.io/guides/security-basic-authentication-howto. + """); + } else { + throw new RuntimeException(""" + HttpAuthenticationMechanism '%s' requires one or more IdentityProviders supporting at least one + of the following credentials types: %s. + Please refer to the https://quarkus.io/guides/security-identity-providers for more information. + """.formatted(mechanism.getClass().getName(), mechanism.getCredentialTypes())); } } if (mechanisms.isEmpty()) {