package com.duosecurity.integrations;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.squareup.okhttp.Response;
import com.duosecurity.duoweb.DuoWeb;
import com.duosecurity.client.Http;
public class DuoShibboleth {
/** Number of tries to attempt a preauth call to Duo. */
private static final int MAX_TRIES = 3;
private final Logger log = LoggerFactory.getLogger(DuoShibboleth.class);
private final String ikey;
private final String skey;
private final String akey;
private final String host;
private final String username;
public static DuoShibboleth instance(String ikey, String skey, String akey, String host, String username) {
if (ikey == null) {
throw new IllegalArgumentException("Null value not allowed for ikey");
}
if (skey == null) {
throw new IllegalArgumentException("Null value not allowed for skey");
}
if (akey == null) {
throw new IllegalArgumentException("Null value not allowed for akey");
}
if (host == null) {
throw new IllegalArgumentException("Null value not allowed for host");
}
if (username == null) {
throw new IllegalArgumentException("Null value not allowed for username");
}
return new DuoShibboleth(ikey.trim(), skey.trim(), akey.trim(), host.trim(), username);
}
private DuoShibboleth() {
this.ikey = null;
this.skey = null;
this.akey = null;
this.host = null;
this.username = null;
}
private DuoShibboleth(String ikey, String skey, String akey, String host, String username) {
this.ikey = ikey;
this.skey = skey;
this.akey = akey;
this.host = host;
this.username = username;
}
private Response sendPreAuthRequest(String username) throws Exception {
Http request = new Http("POST", host, "/auth/v2/preauth", 10);
request.addParam("username", username);
request.signRequest(ikey, skey);
return request.executeHttpRequest();
}
public String performPreauth(String failmode) throws Exception {
if (failmode.equals("secure")) {
return "auth";
} else if (!failmode.equals("safe")) {
throw new IllegalArgumentException("Failmode must be one of either safe or secure.");
}
// Check if Duo authentication is even necessary by calling preauth
for (int i = 0; ; i++) {
try {
Response preAuthResponse = sendPreAuthRequest(username);
int statusCode = preAuthResponse.code();
if (statusCode/100 == 5) {
log.warn("Duo 500 error. Fail open for user:" + username);
return "allow";
}
// parse response
JSONObject json = new JSONObject(preAuthResponse.body().string());
if (!json.getString("stat").equals("OK")) {
throw new Exception(
"Duo error code (" + json.getInt("code") + "): " + json.getString("message"));
}
String result = json.getJSONObject("response").getString("result");
if (result.equals("allow")) {
log.info("Duo 2FA bypass for user:" + username);
return "allow";
}
break;
} catch (java.io.IOException e) {
if (i >= MAX_TRIES-1){
log.warn("Duo server unreachable. Fail open for user:" + username);
return "allow";
}
}
}
return "auth";
}
public String signRequest() {
return DuoWeb.signRequest(ikey, skey, akey, username);
}
public boolean verifyResponseEqualsUsername(String duoResponse) {
try {
String duoVerifiedResponse = DuoWeb.verifyResponse(ikey, skey, akey, duoResponse);
boolean usernameMatches = duoVerifiedResponse.equals(username);
if (usernameMatches) {
log.info(username + " successfully Duo two-factor authenticated.");
return true;
} else {
log.info(username + " attempted Duo two-factor authentication but username " + duoVerifiedResponse + " was sent in the Duo response.");
}
} catch (Exception e) {
log.error("An exception occurred while " + username + " attempted Duo two-factor authentication.", e);
}
return false;
}
}