package oiccli; import com.auth0.jwt.creators.Message; import com.google.common.base.Strings; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.URI; import java.net.URISyntaxException; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.ws.http.HTTPException; import oiccli.HTTP.Response; import oiccli.client_info.ClientInfo; import oiccli.exceptions.MissingEndpoint; import oiccli.exceptions.OicCliError; import oiccli.exceptions.UnsupportedType; import oiccli.exceptions.ValueError; import oiccli.exceptions.WrongContentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Service { private final static Logger logger = LoggerFactory.getLogger(Service.class); private static final List successfulCodes = Arrays.asList(200, 201, 202, 203, 204, 205, 206); private static final List specialArgs = Arrays.asList("authenticationEndpoint", "algs"); private static final Map attributes = new HashMap() {{ put("version", null); put("name", ""); put("value", null); put("port", null); put("isPortSpecified", false); put("domain", ""); put("isDomainSpecified", false); put("domainInitialDot", false); put("path", ""); put("isPathSpecified", false); put("isSecure", false); put("expires", null); put("shouldDiscard", true); put("comment", null); put("commentUrl", null); put("rest", ""); put("rfc2109", true); }}; private Message msgType; private Message responseCls; private ErrorResponse errorResponse; private String endpointName; private boolean isSynchronous = true; private String request; private String defaultAuthenticationMethod; private String httpMethod; private String bodyType; private String responseBodyType; private String endpoint; private String httpLib; private KeyJar keyJar; private String clientAuthenticationMethod; private List events; private Map defaultRequestArgs; private List preConstruct; private List postConstruct; private List postParseResponse; private Map conf; public Service(String httpLib, KeyJar keyJar, String clientAuthenticationMethod, Map conf, Map args) throws NoSuchFieldException, IllegalAccessException { this.httpLib = httpLib; this.keyJar = keyJar; this.clientAuthenticationMethod = clientAuthenticationMethod; this.events = new ArrayList<>(); this.endpoint = ""; this.defaultRequestArgs = new HashMap<>(); if(conf != null) { this.conf = conf; List params = Arrays.asList("msgType", "responseCls", "errorMsg", "defaultAuthenticationMethod", "httpMethod", "bodyType", "responseBodyType"); for(String param : params) { if(conf.containsKey(param)) { this.getClass().getField(param).set(this, conf.get("param")); } } } else { this.conf = new HashMap<>(); } this.preConstruct = new ArrayList<>(); this.postConstruct = new ArrayList<>(); this.postParseResponse = new ArrayList<>(); } public Map gatherRequestArgs(ClientInfo clientInfo, Map args) throws NoSuchFieldException { Map arArgs = new HashMap<>(args); Set properties = this.msgType.getCParam().keySet(); Field field; for(String property : properties) { if(!arArgs.containsKey(property)) { field = clientInfo.getClass().getField(property); if(field != null) { arArgs.put(property, field); } else { field = this.conf.getRequestArgs().getProp(); if(field != null) { arArgs.put(property, field); } else { arArgs.put(property, this.defaultRequestArgs.get(property)); } } } } return arArgs; } public static Map updateHttpArgs(Map httpArgs, Map> info) { Map hArgs = info.get("hArgs"); if (hArgs == null) { hArgs = new HashMap<>(); } if (httpArgs == null) { httpArgs = hArgs; } else { httpArgs.update(info.get("hArgs")); } final String headers = info.get("kwargs").get("headers"); Map hMap = new HashMap() {{ put("headers", headers); }}; httpArgs.update(hMap); info.put("httpArgs", httpArgs); return info; } public Map parseArgs(ClientInfo clientInfo, Map args) throws NoSuchFieldException, IllegalAccessException { Map arArgs = new HashMap<>(args); Object value; for (String property : this.msgType.cParam.keySet()) { if (!arArgs.containsKey(property)) { value = clientInfo.getClass().getField(property).get(property); if (value != null) { arArgs.put(property, value); } else { arArgs.put(property, this.defaultRequestArgs.get(property)); } } } return arArgs; } public List> doPreConstruct(ClientInfo clientInfo, Map requestArgs, Map args) { Map postArgs = new HashMap<>(); for (Object method : this.preConstruct) { /*request_args, _post_args = meth(cli_info, request_args, **kwargs) post_args.update(_post_args)*/ } return Arrays.asList(requestArgs, postArgs); } public Message doPostConstruct(ClientInfo clientInfo, Map requestArgs, Map postArgs) { } public void doPostParseResponse(Message response, ClientInfo clientInfo, String state, Map args) { /* for meth in self.post_parse_response: meth(resp, cli_info, state=state, **kwargs) */ } public Message constructMessage(ClientInfo clientInfo, Map requestArgs, Map args) throws NoSuchFieldException, IllegalAccessException { if (requestArgs == null) { requestArgs = new HashMap<>(); } List> returnedArgs = this.doPreConstruct(clientInfo, requestArgs, args); if (!this.msgType.getCParam().containsKey("state")) { args.remove("state"); } Map argsParam = this.gatherRequestArgs(clientInfo, requestArgs); this.msgType(argsParam); return this.doPostConstruct(clientInfo, requestArgs, returnedArgs.get(1)); } public String getEndpoint(Map args) throws MissingEndpoint { String uri = args.get("endpoint"); if (uri != null) { args.remove("endpoint"); } else { if (!Strings.isNullOrEmpty(endpoint)) { uri = this.endpoint; } else { throw new MissingEndpoint("No endpoint specified"); } } return uri; } public Map uriAndBody(Message cis, String method, Map args) throws UnsupportedEncodingException, UnsupportedType, MissingEndpoint { String uri = this.getEndpoint(args); Map response = Util.getOrPost(uri, method, cis, args); response.put("cis", cis); Map hMap = new HashMap<>(); hMap.put("headers", response.get("headers")); response.put("hArgs", hMap); return response; } public Map initAuthenticationMethod(Message cis, ClientInfo clientInfo, String authenticationMethod, Map httpArgs, Map args) { return initAuthenticationMethod(cis, clientInfo, authenticationMethod, httpArgs, null, args); } public Map initAuthenticationMethod(Message cis, ClientInfo clientInfo, String authenticationMethod, Map requestArgs, Map httpArgs, Map args) { if (httpArgs == null) { httpArgs = new HashMap<>(); } if (!Strings.isNullOrEmpty(authenticationMethod)) { //return this.client_authn_method[authn_method]().construct( // cis, cli_info, request_args, http_args, **kwargs); } else { return httpArgs; } } public Map requestInfo(ClientInfo clientInfo, String method, Map requestArgs, String bodyType, String authenticationMethod, boolean lax, Map args) throws NoSuchFieldException, IllegalAccessException, MissingEndpoint, UnsupportedEncodingException, UnsupportedType { if (method == null) { method = this.httpMethod; } if (requestArgs == null) { requestArgs = new HashMap<>(); } Map hMap = new HashMap<>(); for (String key : args.keySet()) { if (args.get(key) != null && !specialArgs.contains(key)) { hMap.put(key, args.get(key)); } } Message cis = this.constructMessage(clientInfo, requestArgs, args); if (this.events != null) { this.events.store("Protocol request", cis); } cis.setLax(lax); Map hArg = new HashMap<>(); if (!Strings.isNullOrEmpty(authenticationMethod)) { hArg = this.initAuthenticationMethod(cis, clientInfo, authenticationMethod, requestArgs, args); } if (hArg != null) { if (args.containsKey("headers")) { args.get("headers").update(hArg.get("headers")); } else { args.put("headers", hArg.get("headers")); } } if (bodyType.equals("json")) { args.put("contentType", "application/json"); } return this.uriAndBody(cis, method, args); } public Map updateHttpArgs(Map httpArgs, Map info) { Map hArgs = info.get("hArgs"); if (hArgs == null) { hArgs = new HashMap<>(); } if (httpArgs == null) { httpArgs = hArgs; } else { httpArgs.update(info.get("h_args")); } info.put("httpArgs", httpArgs); return info; } public Map doRequestInit(ClientInfo clientInfo, String bodyType, String method, String authenticationMethod, Map requestArgs, Map httpArgs, Map args) throws NoSuchFieldException, IllegalAccessException, MissingEndpoint, UnsupportedEncodingException, UnsupportedType { if (Strings.isNullOrEmpty(method)) { method = this.httpMethod; } if (Strings.isNullOrEmpty(authenticationMethod)) { authenticationMethod = this.defaultAuthenticationMethod; } if (Strings.isNullOrEmpty(bodyType)) { bodyType = this.bodyType; } Map info = this.requestInfo(clientInfo, method, requestArgs, bodyType, authenticationMethod, false, args); return this.updateHttpArgs(httpArgs, info); } public String getUrlInfo(String info) throws URISyntaxException { String query = null; String fragment = null; if (info.contains("?") || info.contains("#")) { URI uri = new URI(info); query = uri.getQuery(); fragment = uri.getFragment(); } if (!Strings.isNullOrEmpty(query)) { return query; } else { return fragment; } } public Message parseResponse(String info, ClientInfo clientInfo, String sFormat, String state, Map args) throws URISyntaxException, OicCliError { logger.debug("Response format: " + sFormat); if (sFormat.equals("urlencoded")) { info = this.getUrlInfo(info); } if (this.events != null) { this.events.store("Response", info); } logger.debug("Response cls: " + this.responseCls.getClass()); Message response = this.responseCls.deserialize(info, sFormat, args); String message = "Initial response parsing => \"{}\""; logger.debug(message.format(response.toDict())); if (this.events != null) { this.events.store("Protocol Response", response); } List errorMsgs; if (response.containsKey("error") && !(response instanceof ErrorResponse)) { response = null; errorMsgs = Arrays.asList(this.errorResponse); for (ErrorResponse errorMsg : errorMsgs) { response = errorMsg.deserialize(info, sFormat); response.verify(); break; } logger.debug("Error response: " + response); } else { args.put("clientId", clientInfo.getClientId()); args.put("issuer", clientInfo.getIssuer()); if (!args.containsKey("key") && !args.containsKey("keyJar")) { args.put("keyJar", keyJar); } logger.debug("Verify response with " + args); boolean isVerificationSuccessful = response.verify(args); if (!isVerificationSuccessful) { logger.error("Verification of the response failed"); throw new OicCliError("Verification of the response failed"); } if (response.getType().equals("AuthorizationResponse") && response.getScope() == null) { response.setScope(args.get("scope")); } } if (response == null) { this.doPostParseResponse(response, clientInfo, state, args); } return response; } public ErrorResponse parseErrorMessage(Response response, String bodyType) { String bodyTypeResult; if (bodyType.equals("txt")) { bodyTypeResult = "urlencoded"; } else { bodyTypeResult = bodyType; } ErrorResponse errorResponse = this.errorResponse.deserialize(response.getText(), bodyTypeResult); errorResponse.verify(); return errorResponse; } public String getValueType(FakeResponse response, String bodyType) throws WrongContentType, ValueError { if (!Strings.isNullOrEmpty(bodyType)) { return Util.verifyHeader(response, bodyType); } else { return "urlencoded"; } } public static ErrorResponse parseRequestResponse(ErrorResponse response, ClientInfo clientInfo, String responseBodyType, String state, Map args) { int statusCode = response.getStatusCode(); if (successfulCodes.contains(statusCode)) { logger.debug("Response body type " + responseBodyType); String valueType = this.getValueType(response, responseBodyType); logger.debug("Successful response " + response.getText()); return this.parseResponse(response.getText(), clientInfo, valueType, state, args); } else if (statusCode == 302 || statusCode == 303) { return response; } else if (statusCode == 500) { logger.error("(" + statusCode + ")" + response.getText()); throw new ParseException("ERROR: Something went wrong " + response.getText()); } else if (statusCode >= 400 && statusCode < 500) { logger.error("Error response (" + statusCode + "): " + response.getText()); String valueType = this.getValueType(response, responseBodyType); return this.parseErrorMessage(response, valueType); } else { logger.error("Error response (" + statusCode + "):" + response.getText()); throw new HTTPException("HTTP ERROR: " + response.getText() + "[" + statusCode + "]" + " on " + response.getUrl()); } } public static ErrorResponse serviceRequest(String url, String method, String body, String responseBodyType, Map httpArgs, ClientInfo clientInfo, Map args) { if (httpArgs == null) { httpArgs = new HashMap<>(); } logger.debug("Doing request with: URL: " + url + ", method: " + method + ", data: " + body + ", https_args: " + httpArgs); Response response = this.httpLib(url, method, body, httpArgs); if (!args.containsKey("keyjar")) { args.put("keyjar", this.keyJar); } if (responseBodyType == null) { responseBodyType = this.responseBodyType; } return parseRequestResponse(response, clientInfo, responseBodyType, "", args); } public static ErrorResponse serviceRequest(String url, Map args) { return serviceRequest(url, "GET", null, "", null, null, args); } public static ErrorResponse serviceRequest(String url, String method, ClientInfo clientInfo, Map args) { return serviceRequest(url, "GET", null, "", null, null, args); } public static ErrorResponse serviceRequest(String url, String method, ClientInfo clientInfo) { return serviceRequest(url, "GET", null, "", null, null, null); } }