diff --git a/bom/pom.xml b/bom/pom.xml index b25c518db1..2044687f58 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -649,6 +649,11 @@ quarkus.vertx.max-event-loop-execute-time=${max.event-loop.execute-time:20000} + + io.vertx + vertx-proton + 4.5.22 + diff --git a/service-base/src/main/java/org/eclipse/hono/service/auth/AbstractHonoAuthenticationService.java b/service-base/src/main/java/org/eclipse/hono/service/auth/AbstractHonoAuthenticationService.java index 7e56436f6c..6e0c20beee 100644 --- a/service-base/src/main/java/org/eclipse/hono/service/auth/AbstractHonoAuthenticationService.java +++ b/service-base/src/main/java/org/eclipse/hono/service/auth/AbstractHonoAuthenticationService.java @@ -117,7 +117,7 @@ public final Future authenticate(final JsonObject authRequest) { final String authzid = new String(authRequest.getBinary(AuthenticationConstants.FIELD_SASL_RESPONSE), StandardCharsets.UTF_8); final String subject = authRequest.getString(AuthenticationConstants.FIELD_SUBJECT_DN); - log.debug("processing EXTERNAL authentication request [Subject DN: {}]", subject); + log.debug("processing EXTERNAL authentication request [Subject DN: {}, Authz ID: {}]", subject, authzid); return verifyExternal(authzid, subject); } else { diff --git a/service-base/src/main/java/org/eclipse/hono/service/auth/delegating/AuthenticationServerClient.java b/service-base/src/main/java/org/eclipse/hono/service/auth/delegating/AuthenticationServerClient.java index d54482e1b5..a4a2c78a2b 100644 --- a/service-base/src/main/java/org/eclipse/hono/service/auth/delegating/AuthenticationServerClient.java +++ b/service-base/src/main/java/org/eclipse/hono/service/auth/delegating/AuthenticationServerClient.java @@ -66,9 +66,6 @@ public AuthenticationServerClient( /** * Verifies a Subject DN with a remote authentication server using SASL EXTERNAL. - *

- * This method currently always fails the handler because there is no way (yet) in vertx-proton - * to perform a SASL EXTERNAL exchange including an authorization id. * * @param authzid The identity to act as. * @param subjectDn The Subject DN. @@ -76,10 +73,23 @@ public AuthenticationServerClient( * the result contains a JWT with the authenticated user's claims. */ public Future verifyExternal(final String authzid, final String subjectDn) { - // unsupported mechanism (until we get better control over client SASL params in vertx-proton) - return Future.failedFuture(new ClientErrorException( - HttpURLConnection.HTTP_BAD_REQUEST, - "unsupported mechanism")); + final ProtonClientOptions options = new ProtonClientOptions(); + options.setReconnectAttempts(3).setReconnectInterval(50); + options.addEnabledSaslMechanism(AuthenticationConstants.MECHANISM_EXTERNAL); + options.setAuthorizationId(AuthenticationConstants.getCommonName(subjectDn)); + + final var connectAttempt = factory.connect(options, null, null); + + return connectAttempt + .compose(openCon -> getToken(openCon)) + .recover(t -> Future.failedFuture(mapConnectionFailureToServiceInvocationException(t))) + .onComplete(s -> { + Optional.ofNullable(connectAttempt.result()) + .ifPresent(con -> { + LOG.debug("closing connection to Authentication service"); + con.close(); + }); + }); } /** diff --git a/services/auth-base/src/main/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationService.java b/services/auth-base/src/main/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationService.java index 1fcd76804d..bbb24f5a7b 100644 --- a/services/auth-base/src/main/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationService.java +++ b/services/auth-base/src/main/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationService.java @@ -280,7 +280,13 @@ private Future verify(final String authenticationId, final JsonObject JsonObject effectiveUser = user; String effectiveAuthorizationId = authenticationId; - if (authorizationId != null && !authorizationId.isEmpty() && isAuthorizedToImpersonate(user)) { + if (authorizationId != null && !authorizationId.isEmpty()) { + if (!isAuthorizedToImpersonate(user)) { + return Future.failedFuture(new ClientErrorException( + HttpURLConnection.HTTP_UNAUTHORIZED, + UNAUTHORIZED)); + } + final JsonObject impersonatedUser = users.get(authorizationId); if (impersonatedUser != null) { effectiveUser = impersonatedUser; diff --git a/services/auth-base/src/test/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationServiceTest.java b/services/auth-base/src/test/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationServiceTest.java index 2a26907237..97833d3520 100644 --- a/services/auth-base/src/test/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationServiceTest.java +++ b/services/auth-base/src/test/java/org/eclipse/hono/authentication/file/FileBasedAuthenticationServiceTest.java @@ -21,11 +21,13 @@ import static com.google.common.truth.Truth.assertThat; import java.io.FileNotFoundException; +import java.net.HttpURLConnection; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.eclipse.hono.auth.Authorities; import org.eclipse.hono.auth.HonoUser; +import org.eclipse.hono.client.ClientErrorException; import org.eclipse.hono.service.auth.AuthTokenFactory; import org.eclipse.hono.util.ResourceIdentifier; import org.junit.jupiter.api.BeforeEach; @@ -187,8 +189,11 @@ public void testVerifyPlainRefusesAuthorizationId(final VertxTestContext ctx) { givenAStartedService() .compose(ok -> authService.verifyPlain("userB", "hono-client@HONO", "secret")) - .onComplete(ctx.succeeding(res -> { - assertUserAndToken(ctx, res, "hono-client@HONO", TOKEN); + .onComplete(ctx.failing(t -> { + ctx.verify(() -> { + assertThat(t).isInstanceOf(ClientErrorException.class); + assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpURLConnection.HTTP_UNAUTHORIZED); + }); ctx.completeNow(); })); } @@ -287,8 +292,11 @@ public void testVerifyExternalRefusesAuthorizationId(final VertxTestContext ctx) givenAStartedService() .compose(ok -> authService.verifyExternal("userB", "CN=userA")) - .onComplete(ctx.succeeding(res -> { - assertUserAndToken(ctx, res, "userA", TOKEN); + .onComplete(ctx.failing(t -> { + ctx.verify(() -> { + assertThat(t).isInstanceOf(ClientErrorException.class); + assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpURLConnection.HTTP_UNAUTHORIZED); + }); ctx.completeNow(); })); }