From 76f4a6c6eda17a9aff74f404fd332e7eaaee7c17 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 8 Jul 2020 10:05:48 +1000 Subject: [PATCH] If no challenge is generated return 401 Fixes #10532 --- .../test/utils/TestIdentityController.java | 6 +-- .../test/utils/TestIdentityProvider.java | 2 +- .../http/security/EmptyChallengeTestCase.java | 53 +++++++++++++++++++ .../http/security/HeaderAuthenticator.java | 44 +++++++++++++++ .../security/TestTrustedIdentityProvider.java | 2 +- .../runtime/security/HttpAuthenticator.java | 25 +++++---- 6 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/EmptyChallengeTestCase.java create mode 100644 extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/HeaderAuthenticator.java diff --git a/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityController.java b/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityController.java index 2b8eb3e90357f..36d84b50ae980 100644 --- a/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityController.java +++ b/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityController.java @@ -8,16 +8,16 @@ public class TestIdentityController { - public static final Map idenitities = new ConcurrentHashMap<>(); + public static final Map identities = new ConcurrentHashMap<>(); public static Builder resetRoles() { - idenitities.clear(); + identities.clear(); return new Builder(); } public static class Builder { public Builder add(String username, String password, String... roles) { - idenitities.put(username, new TestIdentity(username, password, roles)); + identities.put(username, new TestIdentity(username, password, roles)); return this; } } diff --git a/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityProvider.java b/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityProvider.java index 597bddbb73a35..6b3dee03c03c3 100644 --- a/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityProvider.java +++ b/extensions/security/test-utils/src/main/java/io/quarkus/security/test/utils/TestIdentityProvider.java @@ -24,7 +24,7 @@ public Class getRequestType() { @Override public Uni authenticate(UsernamePasswordAuthenticationRequest request, AuthenticationRequestContext context) { - TestIdentityController.TestIdentity ident = TestIdentityController.idenitities.get(request.getUsername()); + TestIdentityController.TestIdentity ident = TestIdentityController.identities.get(request.getUsername()); if (ident == null) { return Uni.createFrom().optional(Optional.empty()); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/EmptyChallengeTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/EmptyChallengeTestCase.java new file mode 100644 index 0000000000000..fbeb2e8dcc6df --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/EmptyChallengeTestCase.java @@ -0,0 +1,53 @@ +package io.quarkus.vertx.http.security; + +import static org.hamcrest.Matchers.equalTo; + +import java.util.function.Supplier; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class EmptyChallengeTestCase { + + private static final String APP_PROPS = "" + + "quarkus.http.auth.permission.roles1.paths=/*\n" + + "quarkus.http.auth.permission.roles1.policy=authenticated\n"; + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(HeaderAuthenticator.class, PathHandler.class) + .addAsResource(new StringAsset(APP_PROPS), "application.properties"); + } + }); + + @Test + public void testNoChallenge() { + + RestAssured + .given() + .header("user", "test") + .when() + .get("/path") + .then() + .assertThat() + .statusCode(200) + .body(equalTo("test:/path")); + RestAssured + .given() + .when() + .get("/path") + .then() + .assertThat() + .statusCode(401); + + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/HeaderAuthenticator.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/HeaderAuthenticator.java new file mode 100644 index 0000000000000..32e13c7e61d79 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/HeaderAuthenticator.java @@ -0,0 +1,44 @@ +package io.quarkus.vertx.http.security; + +import java.util.Collections; +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; + +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.request.AuthenticationRequest; +import io.quarkus.security.runtime.QuarkusPrincipal; +import io.quarkus.security.runtime.QuarkusSecurityIdentity; +import io.quarkus.vertx.http.runtime.security.ChallengeData; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; +import io.quarkus.vertx.http.runtime.security.HttpCredentialTransport; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +@ApplicationScoped +public class HeaderAuthenticator implements HttpAuthenticationMechanism { + @Override + public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) { + String user = context.request().getHeader("user"); + if (user != null) { + return Uni.createFrom().item(QuarkusSecurityIdentity.builder().setPrincipal(new QuarkusPrincipal(user)).build()); + } + return Uni.createFrom().nullItem(); + } + + @Override + public Uni getChallenge(RoutingContext context) { + return Uni.createFrom().nullItem(); + } + + @Override + public Set> getCredentialTypes() { + return Collections.emptySet(); + } + + @Override + public HttpCredentialTransport getCredentialTransport() { + return null; + } +} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/TestTrustedIdentityProvider.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/TestTrustedIdentityProvider.java index d8bb8dfd47c5f..5f5379f93af67 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/TestTrustedIdentityProvider.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/security/TestTrustedIdentityProvider.java @@ -24,7 +24,7 @@ public Class getRequestType() { @Override public Uni authenticate(TrustedAuthenticationRequest request, AuthenticationRequestContext context) { - TestIdentityController.TestIdentity ident = TestIdentityController.idenitities.get(request.getPrincipal()); + TestIdentityController.TestIdentity ident = TestIdentityController.identities.get(request.getPrincipal()); if (ident == null) { return Uni.createFrom().optional(Optional.empty()); } 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 a167f33e110cb..40e4d4ed4dfe4 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 @@ -28,11 +28,10 @@ @ApplicationScoped public class HttpAuthenticator { + final HttpAuthenticationMechanism[] mechanisms; @Inject IdentityProviderManager identityProviderManager; - final HttpAuthenticationMechanism[] mechanisms; - public HttpAuthenticator() { mechanisms = null; } @@ -90,10 +89,10 @@ IdentityProviderManager getIdentityProviderManager() { * Attempts authentication with the contents of the request. If this is possible the Uni * will resolve to a valid SecurityIdentity when it is subscribed to. Note that Uni is lazy, * so this may not happen until the Uni is subscribed to. - * + *

* If invalid credentials are present then the completion stage will resolve to a * {@link io.quarkus.security.AuthenticationFailedException} - * + *

* If no credentials are present it will resolve to null. */ public Uni attemptAuthentication(RoutingContext routingContext) { @@ -116,7 +115,6 @@ public Uni apply(SecurityIdentity data) { } /** - * * @return */ public Uni sendChallenge(RoutingContext routingContext) { @@ -125,15 +123,24 @@ public Uni sendChallenge(RoutingContext routingContext) { HttpAuthenticationMechanism mech = mechanisms[i]; result = result.onItem().produceUni(new Function>() { @Override - public Uni apply(Boolean aBoolean) { - if (aBoolean) { - return Uni.createFrom().item(aBoolean); + public Uni apply(Boolean authDone) { + if (authDone) { + return Uni.createFrom().item(authDone); } return mech.sendChallenge(routingContext); } }); } - return result; + return result.onItem().produceUni(new Function>() { + @Override + public Uni apply(Boolean authDone) { + if (!authDone) { + routingContext.response().setStatusCode(401); + routingContext.response().end(); + } + return Uni.createFrom().item(authDone); + } + }); } public Uni getChallenge(RoutingContext routingContext) {