diff --git a/duo-client/src/main/java/com/duosecurity/client/Http.java b/duo-client/src/main/java/com/duosecurity/client/Http.java index ac169b9..e304e35 100644 --- a/duo-client/src/main/java/com/duosecurity/client/Http.java +++ b/duo-client/src/main/java/com/duosecurity/client/Http.java @@ -32,6 +32,7 @@ public class Http { public static final int DEFAULT_TIMEOUT_SECS = 60; private static final int RATE_LIMIT_ERROR_CODE = 429; + private static final String CA_BUNDLE_VERSION = "1.0"; public static final String UserAgentString = "Duo API Java/0.8.1-SNAPSHOT"; private final String method; @@ -223,7 +224,8 @@ protected Http(String inMethod, String inHost, String inUri, int timeout) { headers = new Headers.Builder(); headers.add("Host", host); - headers.add("user-agent", UserAgentString); + headers.add("user-agent", String.format("%s ca_bundle/%s (ca_pinning=%s)", + UserAgentString, CA_BUNDLE_VERSION, "enabled")); CertificatePinner pinner = Util.createPinner(host, DEFAULT_CA_CERTS); @@ -397,6 +399,10 @@ public void addHeader(String name, String value) { headers.add(name, value); } + void setHeader(String name, String value) { + headers.set(name, value); + } + public void addParam(String name, String value) { params.put(name, value); } @@ -675,6 +681,9 @@ public T build() { if (caCerts != null) { duoClient.useCustomCertificates(caCerts); } + String caPinningStatus = disableCaPinning ? "disabled" : "enabled"; + duoClient.setHeader("user-agent", String.format("%s ca_bundle/%s (ca_pinning=%s)", + UserAgentString, CA_BUNDLE_VERSION, caPinningStatus)); if (disableCaPinning) { duoClient.disableCaPinning(); } diff --git a/duo-client/src/test/java/com/duosecurity/client/HttpUserAgentTest.java b/duo-client/src/test/java/com/duosecurity/client/HttpUserAgentTest.java new file mode 100644 index 0000000..5fb81cc --- /dev/null +++ b/duo-client/src/test/java/com/duosecurity/client/HttpUserAgentTest.java @@ -0,0 +1,54 @@ +package com.duosecurity.client; + +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; +import okhttp3.Headers; +import org.junit.Test; + +public class HttpUserAgentTest { + + private String getUserAgent(Http http) throws Exception { + Field headersField = Http.class.getDeclaredField("headers"); + headersField.setAccessible(true); + Headers.Builder headersBuilder = (Headers.Builder) headersField.get(http); + return headersBuilder.build().get("user-agent"); + } + + @Test + public void testDefaultBuilder_includesCaBundleVersion() throws Exception { + Http http = new Http.HttpBuilder("GET", "api-host.duosecurity.com", "/auth/v2/check") + .build(); + + String userAgent = getUserAgent(http); + assertTrue(userAgent.contains("ca_bundle/1.0")); + } + + @Test + public void testDefaultBuilder_includesCaPinningEnabled() throws Exception { + Http http = new Http.HttpBuilder("GET", "api-host.duosecurity.com", "/auth/v2/check") + .build(); + + String userAgent = getUserAgent(http); + assertTrue(userAgent.contains("(ca_pinning=enabled)")); + } + + @Test + public void testDisableCaPinning_includesCaPinningDisabled() throws Exception { + Http http = new Http.HttpBuilder("GET", "api-host.duosecurity.com", "/auth/v2/check") + .disableCaPinning() + .build(); + + String userAgent = getUserAgent(http); + assertTrue(userAgent.contains("(ca_pinning=disabled)")); + } + + @Test + public void testLegacyConstructor_includesCaBundleAndPinningEnabled() throws Exception { + Http http = new Http("GET", "api-host.duosecurity.com", "/auth/v2/check"); + + String userAgent = getUserAgent(http); + assertTrue(userAgent.contains("ca_bundle/1.0")); + assertTrue(userAgent.contains("(ca_pinning=enabled)")); + } +}