common-smartgears/src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java

164 lines
5.3 KiB
Java

package org.gcube.smartgears.handlers.application.request;
import static org.gcube.smartgears.Constants.token_header;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import java.util.Set;
import org.gcube.common.keycloak.KeycloakClient;
import org.gcube.common.keycloak.KeycloakClientException;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.security.ContextBean;
import org.gcube.common.security.ContextBean.Type;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.GCubeSecret;
import org.gcube.common.security.secrets.Secret;
import org.gcube.common.security.secrets.UmaTokenSecret;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.gcube.smartgears.security.SimpleCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestValidator extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
private static final String BEARER_AUTH_PREFIX ="Bearer";
private ApplicationContext appContext;
@Override
public String getName() {
return Constants.request_validation;
}
@Override
public void handleRequest(RequestEvent call) {
log.trace("executing request validator ON REQUEST");
appContext = call.context();
SecretManagerProvider.set(getSecret(call));
validateAgainstLifecycle(call);
rejectUnauthorizedCalls(call);
if (appContext.container().configuration().mode()!=Mode.offline) {
validateScopeCall();
validatePolicy(call);
}
}
@Override
public void handleResponse(ResponseEvent e) {
log.debug("resetting all the Thread local for this call.");
SecretManagerProvider.reset();
}
private void validateAgainstLifecycle(RequestEvent call) {
switch(appContext.lifecycle().state()) {
case stopped :
application_unavailable_error.fire(); break;
case failed:
application_failed_error.fire(); break;
default:
//nothing to do, but avoids warnings
}
}
private void validateScopeCall() {
String context = SecretManagerProvider.get().getContext();
if (context == null) {
log.warn("rejecting unscoped call to {}",appContext.name());
invalid_request_error.fire("call is unscoped");
}
ContextBean bean = new ContextBean(context);
ContainerConfiguration conf = appContext.container().configuration();
Set<String> allowedContexts =appContext.authorizationProvider().getContexts();
if (!allowedContexts.contains(context) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE)
&& allowedContexts.contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",appContext.name(),context,allowedContexts);
invalid_request_error.fire(appContext.name()+" cannot be called in scope "+context);
}
}
private void rejectUnauthorizedCalls(RequestEvent call){
Secret secret = SecretManagerProvider.get();
if (secret == null){
log.warn("rejecting call to {}, authorization required",appContext.name());
RequestError.request_not_authorized_error.fire(appContext.name()+": authorization required");
}
}
@Override
public String toString() {
return getName();
}
private void validatePolicy(RequestEvent call){
//TODO: must be re-thought
}
private Secret getSecret(RequestEvent call){
String token = call.request().getParameter(token_header)==null? call.request().getHeader(token_header):call.request().getParameter(token_header);
String authHeader = call.request().getHeader(Constants.authorization_header);
log.trace("authorization header is {}",authHeader);
log.trace("token header is {}", token);
log.info("d4s-user set to {} ", call.request().getHeader("d4s-user"));
String umaToken = null;
if (authHeader!=null && !authHeader.isEmpty())
if (authHeader.startsWith(BEARER_AUTH_PREFIX))
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
Secret secret = null;
if (umaToken!=null) {
secret = new UmaTokenSecret(umaToken);
SimpleCredentials credentials = (SimpleCredentials)appContext.authorizationProvider().getCredentials();
KeycloakClient client = KeycloakClientFactory.newInstance();
try {
if(!client.isAccessTokenVerified(secret.getContext(), credentials.getClientID(), credentials.getSecret(), umaToken))
RequestError.request_not_authorized_error.fire("access token verification error");
}catch (KeycloakClientException e) {
RequestError.internal_server_error.fire("error contacting keycloak client", e);
}
} else if (token!=null && !token.isEmpty())
try {
secret = new GCubeSecret(token);
}catch(Throwable t) {
RequestError.request_not_authorized_error.fire("gcube token verification error ("+t.getMessage()+")");
}
return secret;
}
}