Skip to content

Commit

Permalink
fix: broken ChainAuthHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
kristian committed Jun 6, 2022
1 parent d62272e commit a9d770c
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.dependency.packagePresentation": "hierarchical",
"java.format.settings.url": "file:./gradle/spotless/eclipse-formatter.xml",
"java.format.settings.url": "./gradle/spotless/eclipse-formatter.xml",
"java.saveActions.organizeImports": true,
"editor.formatOnSave": true,
"html.format.enable": false,
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/neonbee/endpoint/MountableEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import io.neonbee.config.AuthHandlerConfig;
import io.neonbee.config.EndpointConfig;
import io.neonbee.config.ServerConfig;
import io.neonbee.internal.handler.AuthChainHandler;
import io.neonbee.internal.handler.ChainAuthHandler;
import io.neonbee.internal.handler.HooksHandler;
import io.neonbee.internal.helper.AsyncHelper;
import io.neonbee.logging.LoggingFacade;
Expand Down Expand Up @@ -88,15 +88,15 @@ private MountableEndpoint(EndpointConfig endpointConfig, Endpoint endpoint, Rout
* @param rootRouter the router on which the endpoint will be mounted
* @param defaultAuthHandler the default auth handler if there is no endpoint-specific one
*/
public void mount(Vertx vertx, Router rootRouter, Optional<AuthChainHandler> defaultAuthHandler) {
public void mount(Vertx vertx, Router rootRouter, Optional<ChainAuthHandler> defaultAuthHandler) {
String endpointBasePath = getEndpointBasePath(endpointConfig, endpoint);
Route endpointRoute = rootRouter.route(endpointBasePath + "*");
endpointRoute.handler(new HooksHandler());

Optional<List<AuthHandlerConfig>> effectiveAuthChainConfig =
Optional.ofNullable(endpointConfig.getAuthChainConfig())
.or(() -> Optional.ofNullable(endpoint.getDefaultConfig().getAuthChainConfig()));
effectiveAuthChainConfig.map(authChainConfig -> AuthChainHandler.create(vertx, authChainConfig))
effectiveAuthChainConfig.map(authChainConfig -> ChainAuthHandler.create(vertx, authChainConfig))
.or(() -> defaultAuthHandler).ifPresent(endpointRoute::handler);

if (LOGGER.isInfoEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;

import io.neonbee.config.AuthHandlerConfig;
import io.vertx.core.Vertx;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.ChainAuthHandler;

public interface AuthChainHandler extends AuthenticationHandler {
public interface ChainAuthHandler extends AuthenticationHandler {
/**
* A no operation authentication handler.
*/
AuthChainHandler NOOP_AUTHENTICATION_HANDLER = RoutingContext::next;
@VisibleForTesting
ChainAuthHandler NOOP_AUTHENTICATION_HANDLER = RoutingContext::next;

/**
* Creates an AuthChainHandler instance.
Expand All @@ -23,16 +25,21 @@ public interface AuthChainHandler extends AuthenticationHandler {
* @return an AuthChainHandler based on the passed configuration. If the passed <i>authChainConfig</i> is null or
* empty a {@link #NOOP_AUTHENTICATION_HANDLER} will be returned.
*/
static AuthChainHandler create(Vertx vertx, List<AuthHandlerConfig> authChainConfig) {
static ChainAuthHandler create(Vertx vertx, List<AuthHandlerConfig> authChainConfig) {
if (authChainConfig == null || authChainConfig.isEmpty()) {
return NOOP_AUTHENTICATION_HANDLER;
}

ChainAuthHandler authChainHandler = ChainAuthHandler.any();
io.vertx.ext.web.handler.ChainAuthHandler chainAuthHandler = io.vertx.ext.web.handler.ChainAuthHandler.any();
List<AuthenticationHandler> authHandlers =
authChainConfig.stream().map(config -> config.createAuthHandler(vertx)).collect(Collectors.toList());
authHandlers.forEach(authChainHandler::add);
authHandlers.forEach(chainAuthHandler::add);

return (AuthChainHandler) authChainHandler;
return new ChainAuthHandler() {
@Override
public void handle(RoutingContext event) {
chainAuthHandler.handle(event);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import io.neonbee.endpoint.Endpoint;
import io.neonbee.endpoint.MountableEndpoint;
import io.neonbee.handler.ErrorHandler;
import io.neonbee.internal.handler.AuthChainHandler;
import io.neonbee.internal.handler.CacheControlHandler;
import io.neonbee.internal.handler.ChainAuthHandler;
import io.neonbee.internal.handler.CorrelationIdHandler;
import io.neonbee.internal.handler.DefaultErrorHandler;
import io.neonbee.internal.handler.InstanceInfoHandler;
Expand Down Expand Up @@ -69,8 +69,8 @@ public void start(Promise<Void> startPromise) {

ServerConfig config = new ServerConfig(config());
createRouter(config).compose(router -> {
Optional<AuthChainHandler> defaultAuthHandler =
Optional.ofNullable(config.getAuthChainConfig()).map(c -> AuthChainHandler.create(vertx, c));
Optional<ChainAuthHandler> defaultAuthHandler =
Optional.ofNullable(config.getAuthChainConfig()).map(c -> ChainAuthHandler.create(vertx, c));
return mountEndpoints(router, config.getEndpointConfigs(), defaultAuthHandler).onSuccess(v -> {
// the NotFoundHandler fails the routing context finally.
// To ensure that no handler will be added after it, it is added here.
Expand Down Expand Up @@ -165,7 +165,7 @@ private Future<HttpServer> createHttpServer(Router router, ServerConfig config)
*/
@VisibleForTesting
protected Future<Void> mountEndpoints(Router router, List<EndpointConfig> endpointConfigs,
Optional<AuthChainHandler> defaultAuthHandler) {
Optional<ChainAuthHandler> defaultAuthHandler) {
if (endpointConfigs.isEmpty()) {
LOGGER.warn("No endpoints configured");
return succeededFuture();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.neonbee.internal.handler;

import static com.google.common.truth.Truth.assertThat;
import static io.neonbee.internal.handler.ChainAuthHandler.NOOP_AUTHENTICATION_HANDLER;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import io.neonbee.config.AuthHandlerConfig;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.HttpException;
import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
import io.vertx.junit5.VertxExtension;

@ExtendWith(VertxExtension.class)
class ChainAuthHandlerTest {
@Test
@DisplayName("create empty ChainAuthHandler")
void emptyChainAuthHandlerTest() {
assertThat(ChainAuthHandler.create(null, null)).isSameInstanceAs(NOOP_AUTHENTICATION_HANDLER);
assertThat(ChainAuthHandler.create(null, List.of())).isSameInstanceAs(NOOP_AUTHENTICATION_HANDLER);
}

@Test
@DisplayName("create a non-empty ChainAuthHandler")
void createChainAuthHandlerTest(Vertx vertx) throws IOException {
AuthHandlerConfig config1 = mock(AuthHandlerConfig.class);
when(config1.createAuthHandler(any())).thenReturn(mock(AuthenticationHandlerInternal.class));

AuthHandlerConfig config2 = mock(AuthHandlerConfig.class);
when(config2.createAuthHandler(any())).thenReturn(mock(AuthenticationHandlerInternal.class));

ChainAuthHandler handler = ChainAuthHandler.create(vertx, List.of(config1, config2));
assertThat(handler).isNotSameInstanceAs(NOOP_AUTHENTICATION_HANDLER);
verify(config1).createAuthHandler(vertx);
verify(config2).createAuthHandler(vertx);
}

@Test
@DisplayName("test ChainAuthHandler with multiple checks")
@SuppressWarnings("unchecked")
void testChainAuthHandler(Vertx vertx) throws IOException {
AtomicBoolean firstHandlerCalled = new AtomicBoolean();
AuthenticationHandlerInternal handler1 = mock(AuthenticationHandlerInternal.class);
AuthHandlerConfig config1 = mock(AuthHandlerConfig.class);
when(config1.createAuthHandler(any())).thenReturn(handler1);
doAnswer(invocation -> {
firstHandlerCalled.set(true);
((Handler<Future<Void>>) invocation.getArgument(1)).handle(Future.failedFuture(new HttpException(401)));
return null;
}).when(handler1).authenticate(any(), any());

AuthenticationHandlerInternal handler2 = mock(AuthenticationHandlerInternal.class);
AuthHandlerConfig config2 = mock(AuthHandlerConfig.class);
when(config2.createAuthHandler(any())).thenReturn(handler2);

RoutingContext routingContextMock = mock(RoutingContext.class);
HttpServerRequest requestMock = mock(HttpServerRequest.class);
when(requestMock.isEnded()).thenReturn(true);
when(routingContextMock.request()).thenReturn(requestMock);

ChainAuthHandler handler = ChainAuthHandler.create(vertx, List.of(config1, config2));
handler.handle(routingContextMock);
assertThat(firstHandlerCalled.get()).isTrue();
verify(handler2).authenticate(eq(routingContextMock), any());
}
}
6 changes: 3 additions & 3 deletions src/test/java/io/neonbee/test/base/NeonBeeTestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
import io.neonbee.entity.EntityVerticle;
import io.neonbee.internal.deploy.DeployableVerticle;
import io.neonbee.internal.deploy.Deployment;
import io.neonbee.internal.handler.AuthChainHandler;
import io.neonbee.internal.handler.ChainAuthHandler;
import io.neonbee.internal.verticle.ServerVerticle;
import io.neonbee.job.JobVerticle;
import io.neonbee.test.helper.ConcurrentHelper;
Expand Down Expand Up @@ -412,7 +412,7 @@ public DummyEntityVerticleFactory createDummyEntityVerticle(FullQualifiedName fq
}

private ServerVerticle createDummyServerVerticle(TestInfo testInfo) {
AuthChainHandler dummyAuthHandler = ctx -> {
ChainAuthHandler dummyAuthHandler = ctx -> {
ctx.setUser(User.create(provideUserPrincipal(testInfo)));
Session session = ctx.session();
if (session != null) {
Expand All @@ -428,7 +428,7 @@ private ServerVerticle createDummyServerVerticle(TestInfo testInfo) {

@Override
protected Future<Void> mountEndpoints(Router router, List<EndpointConfig> endpointConfigs,
Optional<AuthChainHandler> defaultAuthHandler) {
Optional<ChainAuthHandler> defaultAuthHandler) {
return super.mountEndpoints(router, endpointConfigs, Optional.of(dummyAuthHandler));
}
};
Expand Down

0 comments on commit a9d770c

Please sign in to comment.