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.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.handler.AuthHandler;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@Deprecated
public abstract class AuthHandlerImpl implements AuthHandler {
static final String AUTH_PROVIDER_CONTEXT_KEY = "io.vertx.ext.web.handler.AuthHandler.provider";
static final HttpStatusException FORBIDDEN = new HttpStatusException(403);
static final HttpStatusException UNAUTHORIZED = new HttpStatusException(401);
static final HttpStatusException BAD_REQUEST = new HttpStatusException(400);
protected final String realm;
protected final AuthenticationProvider authProvider;
protected final Set<String> authorities = new HashSet<>();
public AuthHandlerImpl(AuthenticationProvider authProvider) {
this(authProvider, "");
}
public AuthHandlerImpl(AuthenticationProvider authProvider, String realm) {
this.authProvider = authProvider;
this.realm = realm;
}
@Override
public AuthHandler addAuthority(String authority) {
authorities.add(authority);
return this;
}
@Override
public AuthHandler addAuthorities(Set<String> authorities) {
this.authorities.addAll(authorities);
return this;
}
@Override
public void authorize(User user, Handler<AsyncResult<Void>> handler) {
int requiredcount = authorities.size();
if (requiredcount > 0) {
if (user == null) {
handler.handle(Future.failedFuture(FORBIDDEN));
return;
}
AtomicInteger count = new AtomicInteger();
AtomicBoolean sentFailure = new AtomicBoolean();
Handler<AsyncResult<Boolean>> authHandler = res -> {
if (res.succeeded()) {
if (res.result()) {
if (count.incrementAndGet() == requiredcount) {
handler.handle(Future.succeededFuture());
}
} else {
if (sentFailure.compareAndSet(false, true)) {
handler.handle(Future.failedFuture(FORBIDDEN));
}
}
} else {
handler.handle(Future.failedFuture(res.cause()));
}
};
for (String authority : authorities) {
if (!sentFailure.get()) {
user.isAuthorized(authority, authHandler);
}
}
} else {
handler.handle(Future.succeededFuture());
}
}
@Override
public void handle(RoutingContext ctx) {
if (handlePreflight(ctx)) {
return;
}
User user = ctx.user();
if (user != null) {
authorizeUser(ctx, user);
return;
}
parseCredentials(ctx, res -> {
if (res.failed()) {
processException(ctx, res.cause());
return;
}
User updatedUser = ctx.user();
if (updatedUser != null) {
Session session = ctx.session();
if (session != null) {
session.regenerateId();
}
authorizeUser(ctx, updatedUser);
return;
}
getAuthProvider(ctx).authenticate(res.result(), authN -> {
if (authN.succeeded()) {
User authenticated = authN.result();
ctx.setUser(authenticated);
Session session = ctx.session();
if (session != null) {
session.regenerateId();
}
authorizeUser(ctx, authenticated);
} else {
String header = authenticateHeader(ctx);
if (header != null) {
ctx.response()
.putHeader("WWW-Authenticate", header);
}
if (authN.cause() instanceof HttpStatusException) {
processException(ctx, authN.cause());
} else {
processException(ctx, new HttpStatusException(401, authN.cause()));
}
}
});
});
}
protected void processException(RoutingContext ctx, Throwable exception) {
if (exception != null) {
if (exception instanceof HttpStatusException) {
final int statusCode = ((HttpStatusException) exception).getStatusCode();
final String payload = ((HttpStatusException) exception).getPayload();
switch (statusCode) {
case 302:
ctx.response()
.putHeader(HttpHeaders.LOCATION, payload)
.setStatusCode(302)
.end("Redirecting to " + payload + ".");
return;
case 401:
String header = authenticateHeader(ctx);
if (header != null) {
ctx.response()
.putHeader("WWW-Authenticate", header);
}
ctx.fail(401, exception);
return;
default:
ctx.fail(statusCode, exception);
return;
}
}
}
ctx.fail(exception);
}
private void authorizeUser(RoutingContext ctx, User user) {
authorize(user, authZ -> {
if (authZ.failed()) {
processException(ctx, authZ.cause());
return;
}
ctx.next();
});
}
private boolean handlePreflight(RoutingContext ctx) {
final HttpServerRequest request = ctx.request();
if (request.method() == HttpMethod.OPTIONS) {
final String accessControlRequestHeader = ctx.request().getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
if (accessControlRequestHeader != null) {
for (String ctrlReq : accessControlRequestHeader.split(",")) {
if (ctrlReq.equalsIgnoreCase("Authorization")) {
ctx.next();
return true;
}
}
}
}
return false;
}
private AuthenticationProvider getAuthProvider(RoutingContext ctx) {
try {
AuthenticationProvider provider = ctx.get(AUTH_PROVIDER_CONTEXT_KEY);
if (provider != null) {
return provider;
}
} catch (RuntimeException e) {
}
return authProvider;
}
}