package io.vertx.serviceproxy;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.eventbus.ReplyFailure;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.authentication.CredentialValidationException;
import io.vertx.ext.auth.authentication.TokenCredentials;
import io.vertx.ext.auth.authorization.Authorization;
import io.vertx.ext.auth.authorization.AuthorizationContext;
import io.vertx.ext.auth.authorization.AuthorizationProvider;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
public class ServiceAuthInterceptor implements Function<Message<JsonObject>, Future<Message<JsonObject>>> {
private AuthenticationProvider authn;
private AuthorizationProvider authz;
private Set<Authorization> authorizations;
public ServiceAuthInterceptor setAuthenticationProvider(AuthenticationProvider provider) {
this.authn = provider;
return this;
}
public ServiceAuthInterceptor setAuthorizationProvider(AuthorizationProvider provider) {
this.authz = provider;
return this;
}
public ServiceAuthInterceptor setAuthorizations(Set<Authorization> authorizations) {
this.authorizations = authorizations;
return this;
}
public ServiceAuthInterceptor addAuthorization(Authorization authorization) {
if (authorizations == null) {
authorizations = new HashSet<>();
}
authorizations.add(authorization);
return this;
}
@Override
public Future<Message<JsonObject>> apply(Message<JsonObject> msg) {
final TokenCredentials authorization = new TokenCredentials(msg.headers().get("auth-token"));
try {
authorization.checkValid(null);
Promise<Message<JsonObject>> promise = Promise.promise();
if (authn == null) {
promise.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 500, "No AuthenticationProvider present"));
return promise.future();
}
authn.authenticate(authorization, authenticate -> {
if (authenticate.failed()) {
promise.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 500, authenticate.cause().getMessage()));
return;
}
final User user = authenticate.result();
if (user == null) {
promise.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 401, "Unauthorized"));
return;
}
if (authorizations == null || authorizations.isEmpty()) {
promise.complete(msg);
return;
}
authz.getAuthorizations(user, getAuthorizations -> {
if (getAuthorizations.failed()) {
promise.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 500, authenticate.cause().getMessage()));
} else {
AuthorizationContext context = AuthorizationContext.create(user);
for (Authorization authority : authorizations) {
if (!authority.match(context)) {
promise.fail(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 403, "Forbidden"));
return;
}
}
promise.complete(msg);
}
});
});
return promise.future();
} catch (CredentialValidationException e) {
return Future.failedFuture(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, 401, "Unauthorized"));
}
}
}