package org.gcube.common.authorization.client.proxy; import static org.gcube.common.authorization.client.Constants.CLIENT_ID_PARAM; import static org.gcube.common.authorization.client.Constants.CONTEXT_PARAM; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; import org.gcube.common.authorization.client.Binder; import org.gcube.common.authorization.client.exceptions.ObjectNotFound; import org.gcube.common.authorization.library.AuthorizationEntry; import org.gcube.common.authorization.library.endpoints.AuthorizationEndpoint; import org.gcube.common.authorization.library.endpoints.AuthorizationEndpointScanner; import org.gcube.common.authorization.library.endpoints.EndpointsContainer; import org.gcube.common.authorization.library.provider.UserInfo; import org.gcube.common.authorization.library.utils.AuthorizationEntryList; import org.gcube.common.authorization.library.utils.ListMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultAuthorizationProxy implements AuthorizationProxy { private static Logger log = LoggerFactory.getLogger(AuthorizationProxy.class); private static Map cache = Collections.synchronizedMap(new HashMap()); private static EndpointsContainer endpoints; public DefaultAuthorizationProxy() { if (endpoints==null) endpoints = AuthorizationEndpointScanner.endpoints(); } private String getInternalEnpoint(int infrastructureHash){ AuthorizationEndpoint ae = getEndpoint(infrastructureHash); return getInternalEnpoint(ae); } private String getInternalEnpoint(AuthorizationEndpoint ae){ StringBuilder endpoint = new StringBuilder(ae.isSecureConnection()?"https://":"http://").append(ae.getHost()).append(":") .append(ae.getPort()).append("/authorization-service/gcube/service"); return endpoint.toString(); } @Override public String resolveTokenByUserAndContext(String user, String context) throws ObjectNotFound, Exception { String methodPath = "/token/resolve/"; int infrastructureHash = Utils.getInfrastructureHashfromContext(context); StringBuilder callUrl = new StringBuilder(getInternalEnpoint(infrastructureHash)).append(methodPath).append(user).append("?context=").append(context); URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "GET"); log.debug("response code for "+callUrl.toString()+" is "+connection.getResponseCode()+" "+connection.getResponseMessage()); if (connection.getResponseCode()==404) throw new ObjectNotFound("token not found"); if (connection.getResponseCode()!=200) throw new Exception("error contacting authorization service (error code is "+connection.getResponseCode()+")"); if (connection.getContentLengthLong()==0) return null; String token= ""; try(BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream)connection.getContent()))){ StringBuilder result = new StringBuilder(); String line; while((line = reader.readLine()) != null) result.append(line); token = result.toString(); } return Utils.addInfrastructureHashToToken(token, infrastructureHash); } @Override public String generateUserToken(UserInfo client, String context) throws Exception { String methodPath = "/token/user"; int infrastructureHash = Utils.getInfrastructureHashfromContext(context); StringBuilder callUrl = new StringBuilder(getInternalEnpoint(infrastructureHash)).append(methodPath).append("?") .append(CONTEXT_PARAM).append("=").append(context); URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "PUT"); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestProperty("Content-type", "application/xml"); try(OutputStream os = new BufferedOutputStream(connection.getOutputStream())){ Binder.getContext().createMarshaller().marshal(client, os); } log.debug("response code for "+callUrl.toString()+" is "+connection.getResponseCode()+" "+connection.getResponseMessage()); if (connection.getResponseCode()!=200) throw new Exception("error contacting authorization service"); String token= ""; try(BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream)connection.getContent()))){ StringBuilder result = new StringBuilder(); String line; while((line = reader.readLine()) != null) result.append(line); token = result.toString(); } return Utils.addInfrastructureHashToToken(token, infrastructureHash); } @Override public void setTokenRoles(String token, List roles) throws Exception { String realToken = Utils.getRealToken(token); String methodPath = String.format("/token/user/%s/roles",realToken); int infrastructureHash = Utils.getInfrastructureHashFromToken(token, endpoints.getDefaultInfrastructure()); StringBuilder callUrl = new StringBuilder(getInternalEnpoint(infrastructureHash)).append(methodPath); URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "PUT"); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestProperty("Content-type", "application/xml"); ListMapper listmapper = new ListMapper(roles); try(OutputStream os = new BufferedOutputStream(connection.getOutputStream())){ Binder.getContext().createMarshaller().marshal(listmapper, os); } log.debug("response code for "+callUrl.toString()+" is "+connection.getResponseCode()+" "+connection.getResponseMessage()); if (connection.getResponseCode()!=200) throw new Exception("error contacting authorization service"); } @Override public void removeAllReleatedToken(String clientId, String context) throws Exception{ String methodPath = "/token/user"; int infrastructureHash = Utils.getInfrastructureHashfromContext(context); StringBuilder callUrl = new StringBuilder(getInternalEnpoint(infrastructureHash)).append(methodPath).append("?") .append(CONTEXT_PARAM).append("=").append(context).append("&").append(CLIENT_ID_PARAM).append("=").append(clientId); URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "DELETE"); //connection.setDoOutput(false); connection.setDoInput(true); log.debug("response code for "+callUrl.toString()+" is "+connection.getResponseCode()+" "+connection.getResponseMessage()); if (connection.getResponseCode()!=200 && connection.getResponseCode()!=204) throw new Exception("error contacting authorization service"); } @Override public AuthorizationEntry get(String token) throws ObjectNotFound, Exception{ String realToken = Utils.getRealToken(token); String maskedToken= String.format("%s********",realToken.substring(0, realToken.length()-8)); int infrastructureHashFromToken = Utils.getInfrastructureHashFromToken(token, endpoints.getDefaultInfrastructure()); AuthorizationEndpoint endpoint = getEndpoint(infrastructureHashFromToken); if (cache.containsKey(realToken) && cache.get(realToken).isValid(endpoint.getClientCacheValidity())){ log.trace("valid entry found in cache for token {}, returning it",maskedToken); return cache.get(realToken).getEntry(); } else log.trace("invalid entry found in cache for token {}, contacting auth service",maskedToken); final String methodPath = "/token/"; StringBuilder callUrl = new StringBuilder(getInternalEnpoint(infrastructureHashFromToken)) .append(methodPath).append(realToken); URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "GET"); connection.setDoInput(true); if (connection.getResponseCode()==404) throw new ObjectNotFound("token "+maskedToken+" not found"); if (connection.getResponseCode()!=200) throw new Exception("error contacting authorization service (error code is "+connection.getResponseCode()+")"); if (connection.getContentLengthLong()==0) return null; try(InputStream stream = (InputStream)connection.getContent();){ AuthorizationEntry entry = (AuthorizationEntry)Binder.getContext().createUnmarshaller().unmarshal(stream); if (entry!=null) cache.put(realToken, new AuthorizationEntryCache(entry)); return entry; } } @Override public List get(List tokens) throws ObjectNotFound, Exception { List realTokens = new ArrayList(); List toReturn = new ArrayList(); AuthorizationEndpoint endpoint = null; for (String token : tokens) { String realToken = Utils.getRealToken(token); if (cache.containsKey(realToken) && cache.get(realToken).isValid(endpoint.getClientCacheValidity())) toReturn.add(cache.get(realToken).getEntry()); else realTokens.add(realToken); if (endpoint==null) { int infrastructureHashFromToken = Utils.getInfrastructureHashFromToken(token, endpoints.getDefaultInfrastructure()); endpoint = getEndpoint(infrastructureHashFromToken); } } final String methodPath = "/token/bunch/?"; StringBuilder callUrl = new StringBuilder(getInternalEnpoint(endpoint)) .append(methodPath); boolean first = true; for (String toAppend : realTokens) { if (first) { callUrl= callUrl.append("token=").append(toAppend); first = false; } else callUrl= callUrl.append("&token=").append(toAppend); } URL url = new URL(callUrl.toString()); HttpURLConnection connection = makeRequest(url, "GET"); connection.setDoInput(true); if (connection.getResponseCode()==404) throw new ObjectNotFound("token not found"); if (connection.getResponseCode()!=200) throw new Exception("error contacting authorization service (error code is "+connection.getResponseCode()+")"); if (connection.getContentLengthLong()==0) return null; try(InputStream stream = (InputStream)connection.getContent();){ AuthorizationEntryList entries = (AuthorizationEntryList)Binder.getContext().createUnmarshaller().unmarshal(stream); return entries.getEntries(); } } private HttpURLConnection makeRequest(URL url, String method) throws Exception{ HttpURLConnection connection; if (url.toString().startsWith("https://")) connection = (HttpsURLConnection)url.openConnection(); else connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod(method); return connection; } @Override public AuthorizationEndpoint getEndpoint(int infrastructureHash) { for (String infra: endpoints.getEndpoints().keySet()) { if (Utils.getInfrastructureHashfromContext(infra)==infrastructureHash) return endpoints.getEndpoints().get(infra); } throw new RuntimeException("Authorization Endpoint not found for the required infrastructure"); } @Override public void setEndpoint(EndpointsContainer newEndpoints) { endpoints = newEndpoints; } }