package io.vertx.ext.web.handler.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.authentication.Credentials;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.ChainAuthHandler;
import io.vertx.ext.web.handler.OAuth2AuthHandler;
import io.vertx.ext.web.handler.RedirectAuthHandler;
import java.util.ArrayList;
import java.util.List;
public class ChainAuthHandlerImpl extends AuthenticationHandlerImpl<AuthenticationProvider> implements ChainAuthHandler {
private final List<AuthenticationHandler> handlers = new ArrayList<>();
private final boolean all;
private boolean willRedirect = false;
public ChainAuthHandlerImpl(boolean all) {
super(null);
this.all = all;
}
@Override
public synchronized ChainAuthHandler add(AuthenticationHandler other) {
if (willRedirect) {
throw new IllegalStateException("Cannot add a handler after a handler known to perform a HTTP redirect [RedirectAuthHandler/Oauth2Handler]");
}
handlers.add(other);
if (other instanceof RedirectAuthHandler || other instanceof OAuth2AuthHandler) {
willRedirect = true;
}
if (other instanceof ChainAuthHandler) {
willRedirect &= ((ChainAuthHandlerImpl) other).willRedirect;
}
return this;
}
@Override
public void parseCredentials(RoutingContext context, Handler<AsyncResult<Credentials>> handler) {
if (handlers.size() == 0) {
handler.handle(Future.failedFuture("No providers in the auth chain."));
} else {
iterate(0, context, null, null, handler);
}
}
private void iterate(final int idx, final RoutingContext ctx, Credentials result, HttpStatusException exception, Handler<AsyncResult<Credentials>> handler) {
if (idx >= handlers.size()) {
if (all) {
if (exception == null) {
handler.handle(Future.succeededFuture(result));
} else {
handler.handle(Future.failedFuture(exception));
}
} else {
handler.handle(Future.failedFuture(exception));
}
return;
}
final AuthenticationHandler authHandler = handlers.get(idx);
authHandler.parseCredentials(ctx, res -> {
if (res.failed()) {
if (all) {
} else {
if (res.cause() instanceof HttpStatusException) {
final HttpStatusException ex = (HttpStatusException) res.cause();
switch (ex.getStatusCode()) {
case 302:
case 400:
case 401:
case 403:
iterate(idx + 1, ctx, null, ex, handler);
return;
}
}
}
handler.handle(Future.failedFuture(res.cause()));
return;
}
if (authHandler instanceof AuthenticationHandlerImpl) {
ctx.put(AuthenticationHandlerImpl.AUTH_PROVIDER_CONTEXT_KEY, ((AuthenticationHandlerImpl<?>) authHandler).getAuthProvider(ctx));
}
if (all) {
iterate(idx + 1, ctx, res.result(), null, handler);
} else {
handler.handle(Future.succeededFuture(res.result()));
}
});
}
@Override
public String authenticateHeader(RoutingContext ctx) {
for (AuthenticationHandler authHandler : handlers) {
String header = authHandler.authenticateHeader(ctx);
if (header != null) {
return header;
}
}
return null;
}
}