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 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 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); } }