package org.apereo.cas.adaptors.duo.authn;
import com.duosecurity.client.Http;
import com.duosecurity.duoweb.DuoWeb;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.tuple.Pair;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProperties;
import org.apereo.cas.util.http.HttpClient;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An abstraction that encapsulates interaction with Duo 2fa authentication service via its public API.
*
* @author Michael Kennedy
* @author Misagh Moayyed
* @author Eric Pierce
* @author Dmitriy Kopylenko
* @since 4.2
*/
public class BasicDuoAuthenticationService extends BaseDuoAuthenticationService {
private static final Logger LOGGER = LoggerFactory.getLogger(BasicDuoAuthenticationService.class);
private static final long serialVersionUID = -6690808348975271382L;
/**
* Creates the duo authentication service.
*
* @param duoProperties Duo authentication properties
* @param httpClient http client used to run the requests
*/
public BasicDuoAuthenticationService(final MultifactorAuthenticationProperties.Duo duoProperties, final HttpClient httpClient) {
super(duoProperties, httpClient);
}
@Override
public String signRequestToken(final String uid) {
return DuoWeb.signRequest(duoProperties.getDuoIntegrationKey(),
duoProperties.getDuoSecretKey(),
duoProperties.getDuoApplicationKey(), uid);
}
@Override
public Pair<Boolean, String> authenticate(final Credential creds) throws Exception {
if (creds instanceof DuoDirectCredential) {
return authenticateDuoCredentialDirect(creds);
}
return authenticateDuoCredential(creds);
}
private Pair<Boolean, String> authenticateDuoCredentialDirect(final Credential crds) {
try {
final DuoDirectCredential credential = DuoDirectCredential.class.cast(crds);
final Principal p = credential.getAuthentication().getPrincipal();
final Http request = buildHttpPostAuthRequest();
signHttpAuthRequest(request, p.getId());
final JSONObject result = (JSONObject) request.executeRequest();
LOGGER.debug("Duo authentication response: [{}]", result);
if ("allow".equalsIgnoreCase(result.getString("result"))) {
return Pair.of(Boolean.TRUE, crds.getId());
}
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
}
return Pair.of(Boolean.FALSE, crds.getId());
}
private Pair<Boolean, String> authenticateDuoCredential(final Credential creds) throws Exception {
final String signedRequestToken = DuoCredential.class.cast(creds).getSignedDuoResponse();
if (StringUtils.isBlank(signedRequestToken)) {
throw new IllegalArgumentException("No signed request token was passed to verify");
}
LOGGER.debug("Calling DuoWeb.verifyResponse with signed request token '[{}]'", signedRequestToken);
final String result = DuoWeb.verifyResponse(duoProperties.getDuoIntegrationKey(),
duoProperties.getDuoSecretKey(),
duoProperties.getDuoApplicationKey(), signedRequestToken);
return Pair.of(Boolean.TRUE, result);
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.appendSuper(super.hashCode())
.toHashCode();
}
}