Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,11 @@ quarkus.vertx.max-event-loop-execute-time=${max.event-loop.execute-time:20000}
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-proton</artifactId>
<version>4.5.22</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public final Future<HonoUser> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,30 @@ public AuthenticationServerClient(

/**
* Verifies a Subject DN with a remote authentication server using SASL EXTERNAL.
* <p>
* 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.
* @return A future indicating the outcome of the authentication attempt. On successful authentication,
* the result contains a JWT with the authenticated user's claims.
*/
public Future<HonoUser> 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();
});
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,13 @@ private Future<HonoUser> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}));
}
Expand Down Expand Up @@ -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();
}));
}
Expand Down