package org.gcube.smartgears.handlers.application.request; import static org.gcube.common.authorization.client.Constants.authorizationService; 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.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import org.gcube.common.authorization.library.PolicyUtils; import org.gcube.common.authorization.library.policies.Policy; import org.gcube.common.authorization.library.policies.PolicyType; import org.gcube.common.authorization.library.policies.ServiceAccess; import org.gcube.common.authorization.library.policies.User2ServicePolicy; import org.gcube.common.authorization.library.policies.UserEntity; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.authorization.library.provider.ServiceIdentifier; import org.gcube.common.authorization.library.utils.Caller; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.common.scope.impl.ScopeBean; import org.gcube.common.scope.impl.ScopeBean.Type; 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.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @XmlRootElement(name = Constants.request_validation) public class RequestValidator extends RequestHandler { @XmlAttribute(required=false, name="oauth") @Deprecated boolean oauthCompatibility = false; private static Logger log = LoggerFactory.getLogger(RequestValidator.class); private ApplicationContext context; @Override public String getName() { return Constants.request_validation; } @Override public void handleRequest(RequestEvent call) { log.trace("executing request validator ON REQUEST"); context = call.context(); validateAgainstLifecycle(call); rejectUnauthorizedCalls(call); if (context.container().configuration().mode()!=Mode.offline) { validateScopeCall(); validatePolicy(ScopeProvider.instance.get(), call); } } private void validateAgainstLifecycle(RequestEvent call) { switch(context.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 scope = ScopeProvider.instance.get(); if (scope == null) { log.warn("rejecting unscoped call to {}",context.name()); invalid_request_error.fire("call is unscoped"); } ScopeBean bean = new ScopeBean(scope); ContainerConfiguration conf = context.container().configuration(); if (!conf.allowedContexts().contains(scope) && !(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) { log.warn("rejecting call to {} in invalid context {}, allowed context are {}",context.name(),scope,context.container().configuration().allowedContexts()); invalid_request_error.fire(context.name()+" cannot be called in scope "+scope); } } private void rejectUnauthorizedCalls(RequestEvent call){ String token = SecurityTokenProvider.instance.get(); String scope = ScopeProvider.instance.get(); if (token == null && scope==null){ log.warn("rejecting call to {}, authorization required",context.name(),token); RequestError.request_not_authorized_error.fire(context.name()+": authorization required"); } } @Override public String toString() { return getName(); } private void validatePolicy(String scope, RequestEvent call){ log.info("accessing policy validator in scope {} ", scope); ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier(); String callerId = AuthorizationProvider.instance.get().getClient().getId(); try { List policies = authorizationService().getPolicies(scope); for (Policy policy: policies) { log.debug("policy: {}", policy.getPolicyAsString() ); if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier ) || isPolicyValidForService(policy.getServiceAccess(), serviceIdentifier)) { boolean toReject = false; UserEntity entity = (((User2ServicePolicy) policy).getEntity()); if (entity.getIdentifier()!=null) toReject = entity.getIdentifier().trim().equals(callerId); else if (entity.getExcludes().isEmpty()) toReject = true; else toReject = !entity.getExcludes().contains(callerId); if (toReject) { log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(), callerId); invalid_request_error.fire("rejecting call to "+context.name()+": "+callerId+" is not allowed to contact the service: "+serviceIdentifier.getServiceName() ); } } } }catch (Exception e) { log.warn("error getting policies from context {}", scope, e); } } //TO resolve an error on Auth Portlet private boolean isPolicyValidForService(ServiceAccess serviceAccess, ServiceIdentifier serviceId) { String policyAsString = serviceAccess.getAsString(); return policyAsString.equals("ALL") || policyAsString.equals(serviceId.getServiceClass()+":ALL:ALL") || policyAsString.equals(serviceId.getServiceClass()+":"+serviceId.getServiceName()+":ALL") || policyAsString.equals(serviceId.getFullIdentifier()); } }