package net.webpasswordsafe.server.plugin.authentication.duosecurity.client; import com.squareup.okhttp.Headers; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; import org.json.JSONObject; public class Http { private String method; private String host; private String uri; private Headers.Builder headers; Map<String, String> params = new HashMap<String, String>(); private Proxy proxy; private int timeout = 60; public static SimpleDateFormat RFC_2822_DATE_FORMAT = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US); public static MediaType FORM_ENCODED = MediaType.parse("application/x-www-form-urlencoded"); public Http(String in_method, String in_host, String in_uri) { method = in_method.toUpperCase(); host = in_host; uri = in_uri; headers = new Headers.Builder(); headers.add("Host", host); proxy = null; } public Http(String in_method, String in_host, String in_uri, int timeout) { this(in_method, in_host, in_uri); this.timeout = timeout; } public Object executeRequest() throws Exception { JSONObject result = new JSONObject(executeRequestRaw()); if (! result.getString("stat").equals("OK")) { throw new Exception("Duo error code (" + result.getInt("code") + "): " + result.getString("message")); } return result.get("response"); } public String executeRequestRaw() throws Exception { Response response = executeHttpRequest(); return response.body().string(); } public Response executeHttpRequest() throws Exception { String url = "https://" + host + uri; String queryString = createQueryString(); Request.Builder builder = new Request.Builder(); if (method.equals("POST")) { builder.post(RequestBody.create(FORM_ENCODED, queryString)); } else if (method.equals("PUT")) { builder.put(RequestBody.create(FORM_ENCODED, queryString)); } else if (method.equals("GET")) { if (queryString.length() > 0) { url += "?" + queryString; } builder.get(); } else if(method.equals("DELETE")) { if (queryString.length() > 0) { url += "?" + queryString; } builder.delete(); } else { throw new UnsupportedOperationException("Unsupported method: " + method); } Request request = builder.url(url) .build(); // Set up client. OkHttpClient httpclient = new OkHttpClient(); if (proxy != null) { httpclient.setProxy(proxy); } httpclient.setConnectTimeout(timeout, TimeUnit.SECONDS); httpclient.setWriteTimeout(timeout, TimeUnit.SECONDS); httpclient.setReadTimeout(timeout, TimeUnit.SECONDS); // finish and execute request builder.headers(headers.build()); return httpclient.newCall(builder.build()) .execute(); } public void signRequest(String ikey, String skey) throws UnsupportedEncodingException { signRequest(ikey, skey, 2); } public void signRequest(String ikey, String skey, int sig_version) throws UnsupportedEncodingException { String date = formatDate(new Date()); String canon = canonRequest(date, sig_version); String sig = signHMAC(skey, canon); String auth = ikey + ":" + sig; String header = "Basic " + Base64.encodeBytes(auth.getBytes()); addHeader("Authorization", header); if (sig_version == 2) { addHeader("Date", date); } } protected String signHMAC(String skey, String msg) { try { byte[] sig_bytes = Util.hmacSha1(skey.getBytes(), msg.getBytes()); String sig = Util.bytes_to_hex(sig_bytes); return sig; } catch (Exception e) { return ""; } } private synchronized String formatDate(Date date) { // Could use ThreadLocal or a pool of format objects instead // depending on the needs of the application. return RFC_2822_DATE_FORMAT.format(date); } public void addHeader(String name, String value) { headers.add(name, value); } public void addParam(String name, String value) { params.put(name, value); } public void setProxy(String host, int port) { proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); } protected String canonRequest(String date, int sig_version) throws UnsupportedEncodingException { String canon = ""; if (sig_version == 2) { canon += date + "\n"; } canon += method.toUpperCase() + "\n"; canon += host.toLowerCase() + "\n"; canon += uri + "\n"; canon += createQueryString(); return canon; } private String createQueryString() throws UnsupportedEncodingException { ArrayList<String> args = new ArrayList<String>(); ArrayList<String> keys = new ArrayList<String>(); for (String key : params.keySet()) { keys.add(key); } Collections.sort(keys); for (String key : keys) { String name = URLEncoder .encode(key, "UTF-8") .replace("+", "%20") .replace("*", "%2A") .replace("%7E", "~"); String value = URLEncoder .encode(params.get(key), "UTF-8") .replace("+", "%20") .replace("*", "%2A") .replace("%7E", "~"); args.add(name + "=" + value); } return Util.join(args.toArray(), "&"); } }