package io.vertx.ext.web.handler.impl;
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.AuthenticationHandler;
public abstract class AuthenticationHandlerImpl<T extends AuthenticationProvider> implements AuthenticationHandler {
static final String AUTH_PROVIDER_CONTEXT_KEY = "io.vertx.ext.web.handler.AuthenticationHandler.provider";
static final HttpStatusException UNAUTHORIZED = new HttpStatusException(401);
static final HttpStatusException BAD_REQUEST = new HttpStatusException(400);
static final HttpStatusException BAD_METHOD = new HttpStatusException(405);
protected final String realm;
protected final T authProvider;
public AuthenticationHandlerImpl(T authProvider) {
this(authProvider, null);
}
public AuthenticationHandlerImpl(T authProvider, String realm) {
this.authProvider = authProvider;
this.realm = realm == null ? null : realm
.replaceAll("\"", "\\\"");
if (this.realm != null &&
(this.realm.indexOf('\r') != -1 || this.realm.indexOf('\n') != -1)) {
throw new IllegalArgumentException("Not allowed [\\r|\\n] characters detected on realm name");
}
}
@Override
public void handle(RoutingContext ctx) {
if (handlePreflight(ctx)) {
return;
}
User user = ctx.user();
if (user != null) {
postAuthentication(ctx);
return;
}
ctx.request().pause();
parseCredentials(ctx, res -> {
if (res.failed()) {
ctx.request().resume();
processException(ctx, res.cause());
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();
}
ctx.request().resume();
postAuthentication(ctx);
} else {
String header = authenticateHeader(ctx);
if (header != null) {
ctx.response()
.putHeader("WWW-Authenticate", header);
}
if (authN.cause() instanceof HttpStatusException) {
ctx.request().resume();
processException(ctx, authN.cause());
} else {
ctx.request().resume();
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 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;
}
protected AuthenticationProvider getAuthProvider(RoutingContext ctx) {
try {
AuthenticationProvider provider = ctx.get(AUTH_PROVIDER_CONTEXT_KEY);
if (provider != null) {
return provider;
}
} catch (RuntimeException e) {
}
return authProvider;
}
}