authorization-service/src/main/java/org/gcube/common/authorizationservice/filters/AuthorizedCallFilter.java

224 lines
8.1 KiB
Java

package org.gcube.common.authorizationservice.filters;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.gcube.accounting.datamodel.UsageRecord.OperationResult;
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
import org.gcube.accounting.persistence.AccountingPersistence;
import org.gcube.accounting.persistence.AccountingPersistenceFactory;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
import org.gcube.common.authorizationservice.configuration.AllowedEntity;
import org.gcube.common.authorizationservice.configuration.AuthorizationConfiguration;
import org.gcube.common.authorizationservice.configuration.AuthorizationRule;
import org.gcube.common.authorizationservice.configuration.ConfigurationHolder;
import org.gcube.common.authorizationservice.util.TokenPersistence;
import org.gcube.common.scope.api.ScopeProvider;
import lombok.extern.slf4j.Slf4j;
@WebFilter(urlPatterns={"/*"}, filterName="authorizationFilter")
@Slf4j
public class AuthorizedCallFilter implements Filter {
private static final String TOKEN_HEADER="gcube-token";
public static final String AUTH_ATTRIBUTE="authorizationInfo";
@Inject
TokenPersistence persistence;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String token = request.getParameter(TOKEN_HEADER)==null?((HttpServletRequest)request).getHeader(TOKEN_HEADER):
request.getParameter(TOKEN_HEADER);
long startTime = System.currentTimeMillis();
String callerIp = ((HttpServletRequest)request).getHeader("x-forwarded-for");
if(callerIp==null)
callerIp=request.getRemoteHost();
log.info("caller ip {}", callerIp);
AuthorizationEntry info = null;
if (token!=null){
info = persistence.getAuthorizationEntry(token);
log.info("call from {} ",info);
} else log.info("call without token");
request.setAttribute(AUTH_ATTRIBUTE, info);
String pathInfo = ((HttpServletRequest) request).getPathInfo();
String servletPath = ((HttpServletRequest) request).getServletPath();
if (pathInfo==null || pathInfo.isEmpty()){
pathInfo = servletPath.replace("/gcube/service", "");
log.info("called path info {} ", pathInfo);
if (pathInfo==null || pathInfo.isEmpty()){
((HttpServletResponse)response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.error("call rejected from filters: invalid path");
generateAccounting("Unknown", "Unknown", callerIp, false, startTime, request.getLocalName());
return;
}
}
if (requiresToken(pathInfo) && token==null ){
((HttpServletResponse)response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
log.error("call rejected from filters, call requires caller token");
generateAccounting("Unknown", "Unknown", callerIp, false, startTime, request.getLocalName());
return;
}
/*X509Certificate certs[] =
(X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");
// ... Test if non-null, non-empty.
X509Certificate clientCert = certs[0];
// Get the Subject DN's X500Principal
X500Principal subjectDN = clientCert.getSubjectX500Principal();*/
if (!checkAllowed(pathInfo, callerIp, info)){
((HttpServletResponse)response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
log.error("call rejected from filters");
generateAccounting("Unknown", "Unknown", callerIp, false, startTime, request.getLocalName());
return;
}
chain.doFilter(request, response);
generateAccounting("Unknown", "Unknown", callerIp, true, startTime, request.getLocalName());
}
private boolean requiresToken(String pathInfo) {
AuthorizationConfiguration conf = ConfigurationHolder.getConfiguration();
List<AuthorizationRule> rules = conf.getAuthorizationRules();
for (AuthorizationRule rule: rules)
if (pathInfo.startsWith(rule.getServletPath()) || pathInfo.equals(rule.getServletPath()))
return rule.isTokenRequired();
return false;
}
//TODO: review it, something is not working check if is correct that they are in OR
private boolean checkAllowed(String pathInfo, String callerIp, AuthorizationEntry info){
AuthorizationConfiguration conf = ConfigurationHolder.getConfiguration();
List<AuthorizationRule> rules = conf.getAuthorizationRules();
for (AuthorizationRule rule: rules){
if (pathInfo.startsWith(rule.getServletPath()) || pathInfo.equals(rule.getServletPath())){
if (!rule.getAcceptedTokenType().isEmpty() && !rule.getAcceptedTokenType().contains(info.getClientInfo().getType())){
log.info("rejecting the call: callerType {} is not in the allowed types defined {} ", info.getClientInfo().getType(), rule.getAcceptedTokenType());
return false;
}
if (!rule.getEntities().isEmpty()){
for (AllowedEntity entity : rule.getEntities()){
switch(entity.getType()){
case IP:
log.trace("checking ip rule : {} -> {}", entity.getValue(), callerIp);
if(checkIpInRange(callerIp, entity.getValue()))
return true;
break;
case USER:
log.trace("checking user rule : {} -> {}", entity.getValue(), info.getClientInfo().getId());
if (entity.getValue().equals(info.getClientInfo().getId()))
return true;
break;
case ROLE:
log.trace("checking role rule : {} -> {}", entity.getValue(), info.getClientInfo().getRoles());
if (info.getClientInfo().getRoles().contains(entity.getValue()))
return true;
break;
}
}
//IF a servlet path matches the caller is not allowed to that servlet (the call should be rejected)
return false;
}
}
}
return true;
}
private static boolean checkIpInRange(String ip, String mask) {
String[] maskArray = mask.split("\\.");
String[] ipArray = ip.split("\\.");
int[] maskByte= new int[4];
int[] ipByte = new int[4];
for (int i=0; i<4; i++){
maskByte[i] = ((Integer)Integer.parseInt(maskArray[i])).byteValue();
ipByte[i] = ((Integer)Integer.parseInt(ipArray[i])).byteValue();
}
return (maskByte[0]==0 || maskByte[0]==ipByte[0]) && (maskByte[1]==0 || maskByte[1]==ipByte[1]) &&
(maskByte[2]==0 || maskByte[2]==ipByte[2]) && (maskByte[3]==0 || maskByte[3]==ipByte[3]);
}
@Override
public void destroy() {}
void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success, long startTime, String host){
/*AuthorizationConfiguration conf = ConfigurationHolder.getConfiguration();
AccountingPersistenceFactory.setFallbackLocation(conf.getAccountingDir());
AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
try{
serviceUsageRecord.setConsumerId(caller);
serviceUsageRecord.setCallerQualifier(callerQualifier);
serviceUsageRecord.setScope(ScopeProvider.instance.get());
serviceUsageRecord.setServiceClass("Common");
serviceUsageRecord.setServiceName("Authorization");
serviceUsageRecord.setHost(host);
serviceUsageRecord.setCalledMethod(CalledMethodProvider.instance.get());
serviceUsageRecord.setCallerHost(remoteHost);
serviceUsageRecord.setOperationResult(success?OperationResult.SUCCESS:OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis()-startTime);
persistence.account(serviceUsageRecord);
}catch(Exception ex){
log.warn("invalid record passed to accounting ",ex);
}
*/
log.info("REQUEST SERVED for method {} in {} ms with result {}", CalledMethodProvider.instance.get(), System.currentTimeMillis()-startTime, success?OperationResult.SUCCESS:OperationResult.FAILED);
}
}