From 232a2dd8ea28fd953de21d5cc68f173e39df3bfb Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Mon, 6 Nov 2017 10:47:14 +0800 Subject: [PATCH 1/7] Add support for TLS1.2 on pre-lollipop devices. --- auth0/build.gradle | 1 + .../AuthenticationAPIClient.java | 6 ++ .../android/management/UsersAPIClient.java | 7 ++ .../auth0/android/util/OkHttpTls12Compat.java | 57 +++++++++++++++ .../android/util/Tls12SocketFactory.java | 71 +++++++++++++++++++ .../AuthenticationAPIClientTest.java | 17 +++++ .../management/UsersAPIClientTest.java | 23 ++++++ 7 files changed, 182 insertions(+) create mode 100644 auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java create mode 100644 auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java diff --git a/auth0/build.gradle b/auth0/build.gradle index 40a7ac9b2..b88f8005e 100644 --- a/auth0/build.gradle +++ b/auth0/build.gradle @@ -80,6 +80,7 @@ dependencies { testCompile 'org.hamcrest:java-hamcrest:2.0.0.0' testCompile 'org.powermock:powermock-module-junit4:1.6.5' testCompile 'org.powermock:powermock-module-junit4-rule:1.6.5' + testCompile 'org.powermock:powermock-classloading-xstream:1.6.5' testCompile 'org.powermock:powermock-api-mockito:1.6.5' testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'com.squareup.okhttp:mockwebserver:2.7.5' diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java index 50cb8079e..4f6757f32 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java @@ -45,6 +45,7 @@ import com.auth0.android.result.DatabaseUser; import com.auth0.android.result.Delegation; import com.auth0.android.result.UserProfile; +import com.auth0.android.util.OkHttpTls12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.squareup.okhttp.HttpUrl; @@ -160,6 +161,11 @@ public void setUserAgent(String userAgent) { factory.setUserAgent(userAgent); } + @SuppressWarnings("unused") + public void enableTls12OnPreLollipop() { + OkHttpTls12Compat.enableSupportOnPreLollipop(client); + } + /** * Log in a user with email/username and password for a connection/realm. * In OIDC conformant mode ({@link Auth0#isOIDCConformant()}) it will use the password-realm grant type for the {@code /oauth/token} endpoint diff --git a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java index 3ecbfcc1f..152a6ee65 100755 --- a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java @@ -37,6 +37,7 @@ import com.auth0.android.request.internal.RequestFactory; import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; +import com.auth0.android.util.OkHttpTls12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -132,6 +133,12 @@ public void setUserAgent(String userAgent) { factory.setUserAgent(userAgent); } + @SuppressWarnings("unused") + public void enableTls12OnPreLollipop() { + OkHttpTls12Compat.enableSupportOnPreLollipop(client); + } + + /** * Link a user identity calling '/api/v2/users/:primaryUserId/identities' endpoint diff --git a/auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java b/auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java new file mode 100644 index 000000000..06418f3d4 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java @@ -0,0 +1,57 @@ +package com.auth0.android.util; + +import android.os.Build; +import android.util.Log; + +import com.squareup.okhttp.ConnectionSpec; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.TlsVersion; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.SSLContext; + +public final class OkHttpTls12Compat { + + private static final String TAG = OkHttpTls12Compat.class.getSimpleName(); + + private OkHttpTls12Compat() {} + + /** + * Enable TLS 1.2 on the OkHttpClient on API 16-21, which is supported but not enabled by default. + * @link https://github.com/square/okhttp/issues/2372 + * @see Tls12SocketFactory + * @param client OkHttpClient instance to be modified + */ + public static void enableSupportOnPreLollipop(OkHttpClient client) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { + // No need to modify client as TLS 1.2 is enabled by default on API21+ + // Lollipop is included because some Samsung devices face the same problem on API 21. + return; + } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + Log.w(TAG, "TLS 1.2 not supported on API < 16"); + return; + } + try { + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, null, null); + client.setSslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory())); + + ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .build(); + + List specs = new ArrayList<>(); + specs.add(cs); + specs.add(ConnectionSpec.COMPATIBLE_TLS); + specs.add(ConnectionSpec.CLEARTEXT); + + client.setConnectionSpecs(specs); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + Log.e(TAG, "Error while setting TLS 1.2", e); + } + } +} diff --git a/auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java b/auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java new file mode 100644 index 000000000..df8e30ce7 --- /dev/null +++ b/auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java @@ -0,0 +1,71 @@ +package com.auth0.android.util; + +import android.support.annotation.VisibleForTesting; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +/** + * Enables TLS v1.2 when creating SSLSockets. + *

+ * For some reason, android supports TLS v1.2 from API 16, but enables it by + * default only from API 20. + * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html + * @see SSLSocketFactory + */ +public class Tls12SocketFactory extends SSLSocketFactory { + private static final String[] TLS_V12_ONLY = { "TLSv1.2" }; + + @VisibleForTesting + private final SSLSocketFactory delegate; + + Tls12SocketFactory(SSLSocketFactory base) { + this.delegate = base; + } + + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return patch(delegate.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + return patch(delegate.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return patch(delegate.createSocket(address, port, localAddress, localPort)); + } + + private static Socket patch(Socket s) { + if (s instanceof SSLSocket) { + ((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY); + } + return s; + } +} \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java index 8b08bc5f0..025ed747f 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java @@ -37,6 +37,7 @@ import com.auth0.android.result.UserProfile; import com.auth0.android.util.AuthenticationAPI; import com.auth0.android.util.MockAuthenticationCallback; +import com.auth0.android.util.OkHttpTls12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -49,10 +50,15 @@ import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -87,6 +93,8 @@ @RunWith(RobolectricTestRunner.class) @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@PrepareForTest({OkHttpTls12Compat.class}) public class AuthenticationAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -106,6 +114,9 @@ public class AuthenticationAPIClientTest { private AuthenticationAPI mockAPI; + @Rule + public PowerMockRule rule = new PowerMockRule(); + @Before public void setUp() throws Exception { mockAPI = new AuthenticationAPI(); @@ -1774,6 +1785,12 @@ public void shouldParseUnauthorizedPKCEError() throws Exception { assertThat(callback.getError().getDescription(), is(equalTo("Unauthorized"))); } + @Test + public void shouldExtendTls12Support() { + PowerMockito.mockStatic(OkHttpTls12Compat.class); + client.enableTls12OnPreLollipop(); + PowerMockito.verifyStatic(); + } private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { final Type mapType = new TypeToken>() { diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java index 3a1fa6afc..bc8bbf113 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java @@ -33,6 +33,7 @@ import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; import com.auth0.android.util.MockManagementCallback; +import com.auth0.android.util.OkHttpTls12Compat; import com.auth0.android.util.Telemetry; import com.auth0.android.util.TypeTokenMatcher; import com.auth0.android.util.UsersAPI; @@ -46,8 +47,16 @@ import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.lang.reflect.Type; import java.util.Arrays; @@ -73,6 +82,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +@RunWith(RobolectricTestRunner.class) +@Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@PrepareForTest({OkHttpTls12Compat.class}) public class UsersAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -96,6 +109,9 @@ public class UsersAPIClientTest { private UsersAPI mockAPI; + @Rule + public PowerMockRule rule = new PowerMockRule(); + @Before public void setUp() throws Exception { mockAPI = new UsersAPI(); @@ -395,6 +411,13 @@ public void shouldGetUserProfileSync() throws Exception { assertThat(result, isA(UserProfile.class)); } + @Test + public void shouldExtendTls12Support() { + PowerMockito.mockStatic(OkHttpTls12Compat.class); + client.enableTls12OnPreLollipop(); + PowerMockito.verifyStatic(); + } + private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { final Type mapType = new TypeToken>() { }.getType(); From 557545afcd078c47d803b30b545c731bd73980c3 Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Mon, 6 Nov 2017 11:25:41 +0800 Subject: [PATCH 2/7] Add PowerMockIgnore conditions on API client tests. --- .../authentication/AuthenticationAPIClientTest.java | 3 ++- .../auth0/android/management/UsersAPIClientTest.java | 3 ++- .../mockito/configuration/MockitoConfiguration.java | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 auth0/src/test/java/org/mockito/configuration/MockitoConfiguration.java diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java index 025ed747f..efbe1e2c2 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java @@ -93,7 +93,8 @@ @RunWith(RobolectricTestRunner.class) @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", + "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) @PrepareForTest({OkHttpTls12Compat.class}) public class AuthenticationAPIClientTest { diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java index bc8bbf113..9015b362a 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java @@ -84,7 +84,8 @@ @RunWith(RobolectricTestRunner.class) @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" }) +@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", + "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) @PrepareForTest({OkHttpTls12Compat.class}) public class UsersAPIClientTest { diff --git a/auth0/src/test/java/org/mockito/configuration/MockitoConfiguration.java b/auth0/src/test/java/org/mockito/configuration/MockitoConfiguration.java new file mode 100644 index 000000000..753814984 --- /dev/null +++ b/auth0/src/test/java/org/mockito/configuration/MockitoConfiguration.java @@ -0,0 +1,12 @@ +package org.mockito.configuration; + +public class MockitoConfiguration extends DefaultMockitoConfiguration { + + // Disabling class cache for mockito as suggested in exception message + // https://stackoverflow.com/questions/33008255/classcastexception-exception-when-running-robolectric-test-with-power-mock-on-mu + + @Override + public boolean enableClassCache() { + return false; + } +} \ No newline at end of file From 99f2be0be63308d6f5b2dd66967bf1ee229ab2e4 Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Mon, 6 Nov 2017 16:01:07 +0800 Subject: [PATCH 3/7] Add tests on TLS support utils. --- .../android/util/OkHttpTls12CompatTest.java | 75 +++++++++ .../android/util/Tls12SocketFactoryTest.java | 152 ++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java create mode 100644 auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java diff --git a/auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java b/auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java new file mode 100644 index 000000000..17bbdd40a --- /dev/null +++ b/auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java @@ -0,0 +1,75 @@ +package com.auth0.android.util; + +import android.app.Activity; + +import com.squareup.okhttp.ConnectionSpec; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.TlsVersion; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.List; + +import javax.net.ssl.SSLSocketFactory; + +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@RunWith(RobolectricTestRunner.class) +@Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) +public class OkHttpTls12CompatTest { + + Activity activity; + @Mock OkHttpClient client; + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + activity = Robolectric.setupActivity(Activity.class); + } + + @Test + @Config(sdk=22) + public void shouldNotConfigTlsPostApi21() { + OkHttpTls12Compat.enableSupportOnPreLollipop(client); + verify(client, never()).setSslSocketFactory((SSLSocketFactory) any()); + } + + @Test + public void shouldConfigTlsOnOrPreApi21() { + OkHttpTls12Compat.enableSupportOnPreLollipop(client); + + ArgumentCaptor factoryCaptor = ArgumentCaptor.forClass(SSLSocketFactory.class); + verify(client).setSslSocketFactory(factoryCaptor.capture()); + assertTrue(factoryCaptor.getValue() instanceof Tls12SocketFactory); + + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(List.class); + verify(client).setConnectionSpecs(specCaptor.capture()); + boolean hasTls12 = false; + for (Object item : specCaptor.getValue()) { + assertTrue(item instanceof ConnectionSpec); + ConnectionSpec spec = (ConnectionSpec) item; + if (!spec.isTls()) { + continue; + } + List versions = spec.tlsVersions(); + for (TlsVersion version : versions) { + if ("TLSv1.2".equals(version.javaName())) { + hasTls12 = true; + break; + } + } + } + assertTrue(hasTls12); + } +} diff --git a/auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java b/auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java new file mode 100644 index 000000000..0d426b52b --- /dev/null +++ b/auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java @@ -0,0 +1,152 @@ +package com.auth0.android.util; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Arrays; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class Tls12SocketFactoryTest { + + private static final String TLS_1_2 = "TLSv1.2"; + private static final String MOCK_HOST = "www.example.com"; + private static final int MOCK_PORT = 8080; + private static final int MOCK_LOCAL_PORT = 8081; + private static final boolean MOCK_AUTO_CLOSE = true; + + @Mock SSLSocket socket; + @Mock SSLSocketFactory delegate; + Tls12SocketFactory factory; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + factory = new Tls12SocketFactory(delegate); + } + + @Test + public void shouldGetDefaultCipherSuites() { + String[] suites = new String[]{"Test"}; + when(delegate.getDefaultCipherSuites()).thenReturn(suites); + + String[] result = factory.getDefaultCipherSuites(); + + verify(delegate).getDefaultCipherSuites(); + assertTrue(Arrays.equals(result, suites)); + } + + @Test + public void shouldGetSupportedCipherSuites() { + String[] suites = new String[]{"Test"}; + when(delegate.getSupportedCipherSuites()).thenReturn(suites); + + String[] result = factory.getSupportedCipherSuites(); + + verify(delegate).getSupportedCipherSuites(); + assertTrue(Arrays.equals(result, suites)); + } + + @Test + public void shouldCreateSocket_socket_host_port_autoClose() throws IOException { + when(delegate.createSocket((Socket) anyObject(), anyString(), anyInt(), anyBoolean())) + .thenReturn(socket); + + Socket result = factory.createSocket(socket, MOCK_HOST, MOCK_PORT, MOCK_AUTO_CLOSE); + + assertEquals(result, socket); + verify(delegate).createSocket(eq(socket), eq(MOCK_HOST), eq(MOCK_PORT), eq(MOCK_AUTO_CLOSE)); + verifyPatched(result); + } + + @Test + public void shouldCreateSocket_host_port() throws IOException { + when(delegate.createSocket(anyString(), anyInt())) + .thenReturn(socket); + + Socket result = factory.createSocket(MOCK_HOST, MOCK_PORT); + + assertEquals(result, socket); + verify(delegate).createSocket(eq(MOCK_HOST), eq(MOCK_PORT)); + verifyPatched(result); + } + + @Test + public void shouldCreateSocket_host_port_localHost_localPort() throws IOException { + InetAddress localHost = mock(InetAddress.class); + when(delegate.createSocket(anyString(), anyInt(), (InetAddress) anyObject(), anyInt())) + .thenReturn(socket); + + Socket result = factory.createSocket(MOCK_HOST, MOCK_PORT, localHost, MOCK_LOCAL_PORT); + + assertEquals(result, socket); + verify(delegate).createSocket(eq(MOCK_HOST), eq(MOCK_PORT), eq(localHost), eq(MOCK_LOCAL_PORT)); + verifyPatched(result); + } + + @Test + public void shouldCreateSocket_hostAddress_port() throws IOException { + InetAddress host = mock(InetAddress.class); + when(delegate.createSocket((InetAddress) anyObject(), anyInt())) + .thenReturn(socket); + + Socket result = factory.createSocket(host, MOCK_PORT); + + assertEquals(result, socket); + verify(delegate).createSocket(eq(host), eq(MOCK_PORT)); + verifyPatched(result); + } + + @Test + public void shouldCreateSocket_address_port_localAddress_localPort() throws IOException { + InetAddress address = mock(InetAddress.class); + InetAddress localAddress = mock(InetAddress.class); + when(delegate.createSocket((InetAddress) anyObject(), anyInt(), (InetAddress) anyObject(), anyInt())) + .thenReturn(socket); + + Socket result = factory.createSocket(address, MOCK_PORT, localAddress, MOCK_LOCAL_PORT); + + assertEquals(result, socket); + verify(delegate).createSocket(eq(address), eq(MOCK_PORT), eq(localAddress), eq(MOCK_LOCAL_PORT)); + verifyPatched(result); + } + + + private static void verifyPatched(Socket socket) { + ArgumentCaptor captor = ArgumentCaptor.forClass(String[].class); + assertTrue(socket instanceof SSLSocket); + verify((SSLSocket)socket).setEnabledProtocols(captor.capture()); + String[] protocols = captor.getValue(); + boolean patched = false; + for (String string : protocols) { + if (TLS_1_2.equals(string)) { + patched = true; + break; + } + } + assertTrue(patched); + } +} From f9eb0548dd2263090920974490ebe917023b5ec8 Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Thu, 9 Nov 2017 10:52:51 +0800 Subject: [PATCH 4/7] Rename TLS related methods; make TLS12SocketFactory package private. --- .../AuthenticationAPIClient.java | 6 +++--- .../android/management/UsersAPIClient.java | 8 +++---- ...ls12Compat.java => OkHttpTLS12Compat.java} | 21 ++++++++----------- ...etFactory.java => TLS12SocketFactory.java} | 4 ++-- .../AuthenticationAPIClientTest.java | 8 +++---- .../management/UsersAPIClientTest.java | 8 +++---- ...atTest.java => OkHttpTLS12CompatTest.java} | 9 ++++---- ...yTest.java => TLS12SocketFactoryTest.java} | 6 +++--- 8 files changed, 34 insertions(+), 36 deletions(-) rename auth0/src/main/java/com/auth0/android/util/{OkHttpTls12Compat.java => OkHttpTLS12Compat.java} (65%) rename auth0/src/main/java/com/auth0/android/util/{Tls12SocketFactory.java => TLS12SocketFactory.java} (95%) rename auth0/src/test/java/com/auth0/android/util/{OkHttpTls12CompatTest.java => OkHttpTLS12CompatTest.java} (90%) rename auth0/src/test/java/com/auth0/android/util/{Tls12SocketFactoryTest.java => TLS12SocketFactoryTest.java} (97%) diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java index 4f6757f32..df0467d19 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java @@ -45,7 +45,7 @@ import com.auth0.android.result.DatabaseUser; import com.auth0.android.result.Delegation; import com.auth0.android.result.UserProfile; -import com.auth0.android.util.OkHttpTls12Compat; +import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.squareup.okhttp.HttpUrl; @@ -162,8 +162,8 @@ public void setUserAgent(String userAgent) { } @SuppressWarnings("unused") - public void enableTls12OnPreLollipop() { - OkHttpTls12Compat.enableSupportOnPreLollipop(client); + public void enableTLS12OnPreLollipop() { + OkHttpTLS12Compat.enableForClient(client); } /** diff --git a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java index 152a6ee65..9a4b725eb 100755 --- a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java @@ -37,7 +37,7 @@ import com.auth0.android.request.internal.RequestFactory; import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; -import com.auth0.android.util.OkHttpTls12Compat; +import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -133,9 +133,9 @@ public void setUserAgent(String userAgent) { factory.setUserAgent(userAgent); } - @SuppressWarnings("unused") - public void enableTls12OnPreLollipop() { - OkHttpTls12Compat.enableSupportOnPreLollipop(client); + @SuppressWarnings("Unused") + public void enableTLS12OnPreLollipop() { + OkHttpTLS12Compat.enableForClient(client); } diff --git a/auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java b/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java similarity index 65% rename from auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java rename to auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java index 06418f3d4..ab40811ba 100644 --- a/auth0/src/main/java/com/auth0/android/util/OkHttpTls12Compat.java +++ b/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java @@ -14,31 +14,28 @@ import javax.net.ssl.SSLContext; -public final class OkHttpTls12Compat { +public final class OkHttpTLS12Compat { - private static final String TAG = OkHttpTls12Compat.class.getSimpleName(); + private static final String TAG = OkHttpTLS12Compat.class.getSimpleName(); - private OkHttpTls12Compat() {} + private OkHttpTLS12Compat() {} /** * Enable TLS 1.2 on the OkHttpClient on API 16-21, which is supported but not enabled by default. * @link https://github.com/square/okhttp/issues/2372 - * @see Tls12SocketFactory + * @see TLS12SocketFactory * @param client OkHttpClient instance to be modified */ - public static void enableSupportOnPreLollipop(OkHttpClient client) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - // No need to modify client as TLS 1.2 is enabled by default on API21+ - // Lollipop is included because some Samsung devices face the same problem on API 21. - return; - } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - Log.w(TAG, "TLS 1.2 not supported on API < 16"); + public static void enableForClient(OkHttpClient client) { + // No need to modify client as TLS 1.2 is enabled by default on API21+ + // Lollipop is included because some Samsung devices face the same problem on API 21. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN || Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { return; } try { SSLContext sc = SSLContext.getInstance("TLSv1.2"); sc.init(null, null, null); - client.setSslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory())); + client.setSslSocketFactory(new TLS12SocketFactory(sc.getSocketFactory())); ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_2) diff --git a/auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java b/auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java similarity index 95% rename from auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java rename to auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java index df8e30ce7..1c0e0a05d 100644 --- a/auth0/src/main/java/com/auth0/android/util/Tls12SocketFactory.java +++ b/auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java @@ -17,13 +17,13 @@ * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html * @see SSLSocketFactory */ -public class Tls12SocketFactory extends SSLSocketFactory { +class TLS12SocketFactory extends SSLSocketFactory { private static final String[] TLS_V12_ONLY = { "TLSv1.2" }; @VisibleForTesting private final SSLSocketFactory delegate; - Tls12SocketFactory(SSLSocketFactory base) { + TLS12SocketFactory(SSLSocketFactory base) { this.delegate = base; } diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java index efbe1e2c2..3ec02618f 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java @@ -37,7 +37,7 @@ import com.auth0.android.result.UserProfile; import com.auth0.android.util.AuthenticationAPI; import com.auth0.android.util.MockAuthenticationCallback; -import com.auth0.android.util.OkHttpTls12Compat; +import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -95,7 +95,7 @@ @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) @PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) -@PrepareForTest({OkHttpTls12Compat.class}) +@PrepareForTest({OkHttpTLS12Compat.class}) public class AuthenticationAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -1788,8 +1788,8 @@ public void shouldParseUnauthorizedPKCEError() throws Exception { @Test public void shouldExtendTls12Support() { - PowerMockito.mockStatic(OkHttpTls12Compat.class); - client.enableTls12OnPreLollipop(); + PowerMockito.mockStatic(OkHttpTLS12Compat.class); + client.enableTLS12OnPreLollipop(); PowerMockito.verifyStatic(); } diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java index 9015b362a..e0c0210e4 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java @@ -33,7 +33,7 @@ import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; import com.auth0.android.util.MockManagementCallback; -import com.auth0.android.util.OkHttpTls12Compat; +import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.auth0.android.util.TypeTokenMatcher; import com.auth0.android.util.UsersAPI; @@ -86,7 +86,7 @@ @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) @PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) -@PrepareForTest({OkHttpTls12Compat.class}) +@PrepareForTest({OkHttpTLS12Compat.class}) public class UsersAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -414,8 +414,8 @@ public void shouldGetUserProfileSync() throws Exception { @Test public void shouldExtendTls12Support() { - PowerMockito.mockStatic(OkHttpTls12Compat.class); - client.enableTls12OnPreLollipop(); + PowerMockito.mockStatic(OkHttpTLS12Compat.class); + client.enableTLS12OnPreLollipop(); PowerMockito.verifyStatic(); } diff --git a/auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java b/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java similarity index 90% rename from auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java rename to auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java index 17bbdd40a..bb0dee037 100644 --- a/auth0/src/test/java/com/auth0/android/util/OkHttpTls12CompatTest.java +++ b/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java @@ -27,7 +27,7 @@ @RunWith(RobolectricTestRunner.class) @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -public class OkHttpTls12CompatTest { +public class OkHttpTLS12CompatTest { Activity activity; @Mock OkHttpClient client; @@ -41,17 +41,18 @@ public void setUp(){ @Test @Config(sdk=22) public void shouldNotConfigTlsPostApi21() { - OkHttpTls12Compat.enableSupportOnPreLollipop(client); + OkHttpTLS12Compat.enableForClient(client); verify(client, never()).setSslSocketFactory((SSLSocketFactory) any()); } @Test + @Config(sdk=21) public void shouldConfigTlsOnOrPreApi21() { - OkHttpTls12Compat.enableSupportOnPreLollipop(client); + OkHttpTLS12Compat.enableForClient(client); ArgumentCaptor factoryCaptor = ArgumentCaptor.forClass(SSLSocketFactory.class); verify(client).setSslSocketFactory(factoryCaptor.capture()); - assertTrue(factoryCaptor.getValue() instanceof Tls12SocketFactory); + assertTrue(factoryCaptor.getValue() instanceof TLS12SocketFactory); ArgumentCaptor specCaptor = ArgumentCaptor.forClass(List.class); verify(client).setConnectionSpecs(specCaptor.capture()); diff --git a/auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java b/auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java similarity index 97% rename from auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java rename to auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java index 0d426b52b..caffa5de1 100644 --- a/auth0/src/test/java/com/auth0/android/util/Tls12SocketFactoryTest.java +++ b/auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class Tls12SocketFactoryTest { +public class TLS12SocketFactoryTest { private static final String TLS_1_2 = "TLSv1.2"; private static final String MOCK_HOST = "www.example.com"; @@ -37,7 +37,7 @@ public class Tls12SocketFactoryTest { @Mock SSLSocket socket; @Mock SSLSocketFactory delegate; - Tls12SocketFactory factory; + TLS12SocketFactory factory; @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -45,7 +45,7 @@ public class Tls12SocketFactoryTest { @Before public void setUp(){ MockitoAnnotations.initMocks(this); - factory = new Tls12SocketFactory(delegate); + factory = new TLS12SocketFactory(delegate); } @Test From f0d8ce0a0324598e883a72dd5165807b5f2e1d8b Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Thu, 9 Nov 2017 12:04:30 +0800 Subject: [PATCH 5/7] Make OkHttpTLS12Compat non static. --- .../AuthenticationAPIClient.java | 14 +++--- .../android/management/UsersAPIClient.java | 16 +++---- .../auth0/android/util/OkHttpTLS12Compat.java | 22 ++++++--- .../AuthenticationAPIClientTest.java | 42 +++++++++-------- .../management/UsersAPIClientTest.java | 46 +++++++++---------- .../android/util/OkHttpTLS12CompatTest.java | 10 ++-- 6 files changed, 84 insertions(+), 66 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java index df0467d19..090a4f349 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java @@ -98,7 +98,8 @@ public class AuthenticationAPIClient { private static final String HEADER_AUTHORIZATION = "Authorization"; private final Auth0 auth0; - private final OkHttpClient client; + @VisibleForTesting final OkHttpClient client; + private final OkHttpTLS12Compat tlsCompat; private final Gson gson; private final RequestFactory factory; private final ErrorBuilder authErrorBuilder; @@ -110,7 +111,7 @@ public class AuthenticationAPIClient { * @param auth0 account information */ public AuthenticationAPIClient(@NonNull Auth0 auth0) { - this(auth0, new RequestFactory(), new OkHttpClient(), GsonProvider.buildGson()); + this(auth0, new RequestFactory(), new OkHttpClient(), new OkHttpTLS12Compat(), GsonProvider.buildGson()); } /** @@ -124,13 +125,14 @@ public AuthenticationAPIClient(Context context) { } @VisibleForTesting - AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client) { - this(auth0, factory, client, GsonProvider.buildGson()); + AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat) { + this(auth0, factory, client, tlsCompat, GsonProvider.buildGson()); } - private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) { + private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat, Gson gson) { this.auth0 = auth0; this.client = client; + this.tlsCompat = tlsCompat; if (auth0.isLoggingEnabled()) { this.client.interceptors().add(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)); } @@ -163,7 +165,7 @@ public void setUserAgent(String userAgent) { @SuppressWarnings("unused") public void enableTLS12OnPreLollipop() { - OkHttpTLS12Compat.enableForClient(client); + tlsCompat.setClient(client).enableForClient(); } /** diff --git a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java index 9a4b725eb..3674b8c42 100755 --- a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java @@ -69,7 +69,8 @@ public class UsersAPIClient { private static final String USER_METADATA_KEY = "user_metadata"; private final Auth0 auth0; - private final OkHttpClient client; + private final OkHttpTLS12Compat tlsCompat; + @VisibleForTesting final OkHttpClient client; private final Gson gson; private final RequestFactory factory; private final ErrorBuilder mgmtErrorBuilder; @@ -81,7 +82,7 @@ public class UsersAPIClient { * @param token of the primary identity */ public UsersAPIClient(Auth0 auth0, String token) { - this(auth0, new RequestFactory(token), new OkHttpClient(), GsonProvider.buildGson()); + this(auth0, new RequestFactory(token), new OkHttpClient(), new OkHttpTLS12Compat(), GsonProvider.buildGson()); } /** @@ -96,13 +97,14 @@ public UsersAPIClient(Context context, String token) { } @VisibleForTesting - UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client) { - this(auth0, factory, client, GsonProvider.buildGson()); + UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat) { + this(auth0, factory, client, tlsCompat, GsonProvider.buildGson()); } - private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) { + private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat, Gson gson) { this.auth0 = auth0; this.client = client; + this.tlsCompat = tlsCompat; if (auth0.isLoggingEnabled()) { this.client.interceptors().add(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)); } @@ -135,11 +137,9 @@ public void setUserAgent(String userAgent) { @SuppressWarnings("Unused") public void enableTLS12OnPreLollipop() { - OkHttpTLS12Compat.enableForClient(client); + tlsCompat.setClient(client).enableForClient(); } - - /** * Link a user identity calling '/api/v2/users/:primaryUserId/identities' endpoint * Example usage: diff --git a/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java b/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java index ab40811ba..bbaea8f50 100644 --- a/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java +++ b/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java @@ -14,23 +14,32 @@ import javax.net.ssl.SSLContext; -public final class OkHttpTLS12Compat { +public class OkHttpTLS12Compat { private static final String TAG = OkHttpTLS12Compat.class.getSimpleName(); - private OkHttpTLS12Compat() {} + private OkHttpClient client = null; + + /** + * Sets the OkHttp client instance + * @param client OkHttpClient instance to be modified + */ + public OkHttpTLS12Compat setClient(OkHttpClient client) { + this.client = client; + return this; + } /** * Enable TLS 1.2 on the OkHttpClient on API 16-21, which is supported but not enabled by default. * @link https://github.com/square/okhttp/issues/2372 * @see TLS12SocketFactory - * @param client OkHttpClient instance to be modified */ - public static void enableForClient(OkHttpClient client) { + public OkHttpTLS12Compat enableForClient() { // No need to modify client as TLS 1.2 is enabled by default on API21+ // Lollipop is included because some Samsung devices face the same problem on API 21. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN || Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - return; + if (client == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN + || Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { + return this; } try { SSLContext sc = SSLContext.getInstance("TLSv1.2"); @@ -50,5 +59,6 @@ public static void enableForClient(OkHttpClient client) { } catch (NoSuchAlgorithmException | KeyManagementException e) { Log.e(TAG, "Error while setting TLS 1.2", e); } + return this; } } diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java index 3ec02618f..832742a04 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java @@ -50,15 +50,10 @@ import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -84,6 +79,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -93,9 +89,6 @@ @RunWith(RobolectricTestRunner.class) @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", - "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) -@PrepareForTest({OkHttpTLS12Compat.class}) public class AuthenticationAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -115,9 +108,6 @@ public class AuthenticationAPIClientTest { private AuthenticationAPI mockAPI; - @Rule - public PowerMockRule rule = new PowerMockRule(); - @Before public void setUp() throws Exception { mockAPI = new AuthenticationAPI(); @@ -137,7 +127,8 @@ public void shouldSetUserAgent() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); - AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient, tlsCompat); client.setUserAgent("nexus-5x"); verify(factory).setUserAgent("nexus-5x"); } @@ -148,9 +139,10 @@ public void shouldSetTelemetryIfPresent() throws Exception { when(telemetry.getValue()).thenReturn("the-telemetry-data"); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.setTelemetry(telemetry); - new AuthenticationAPIClient(auth0, factory, okClient); + AuthenticationAPIClient client = new AuthenticationAPIClient(auth0, factory, okClient, tlsCompat); verify(factory).setClientInfo("the-telemetry-data"); } @@ -158,9 +150,10 @@ public void shouldSetTelemetryIfPresent() throws Exception { public void shouldNotSetTelemetryIfMissing() throws Exception { RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.doNotSendTelemetry(); - new AuthenticationAPIClient(auth0, factory, okClient); + new AuthenticationAPIClient(auth0, factory, okClient, tlsCompat); verify(factory, never()).setClientInfo(any(String.class)); } @@ -171,11 +164,12 @@ public void shouldEnableHttpLogging() throws Exception { when(account.isLoggingEnabled()).thenReturn(true); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); ArgumentCaptor interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class); - new AuthenticationAPIClient(account, factory, okClient); + new AuthenticationAPIClient(account, factory, okClient, tlsCompat); verify(okClient).interceptors(); verify(list).add(interceptorCaptor.capture()); @@ -192,10 +186,11 @@ public void shouldDisableHttpLogging() throws Exception { when(account.isLoggingEnabled()).thenReturn(false); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); - new AuthenticationAPIClient(account, factory, okClient); + new AuthenticationAPIClient(account, factory, okClient, tlsCompat); verify(okClient, never()).interceptors(); verify(list, never()).add(any(Interceptor.class)); @@ -207,10 +202,11 @@ public void shouldHaveHttpLoggingDisabledByDefault() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); - new AuthenticationAPIClient(account, factory, okClient); + new AuthenticationAPIClient(account, factory, okClient, tlsCompat); verify(okClient, never()).interceptors(); verify(list, never()).add(any(Interceptor.class)); @@ -1788,9 +1784,17 @@ public void shouldParseUnauthorizedPKCEError() throws Exception { @Test public void shouldExtendTls12Support() { - PowerMockito.mockStatic(OkHttpTLS12Compat.class); + Auth0 account = mock(Auth0.class); + RequestFactory factory = mock(RequestFactory.class); + OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + when(tlsCompat.setClient((OkHttpClient) anyObject())).thenReturn(tlsCompat); + AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient, tlsCompat); + client.enableTLS12OnPreLollipop(); - PowerMockito.verifyStatic(); + + verify(tlsCompat).setClient(eq(client.client)); + verify(tlsCompat).enableForClient(); } private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java index e0c0210e4..e126d5d58 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java @@ -47,16 +47,9 @@ import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import java.lang.reflect.Type; import java.util.Arrays; @@ -75,6 +68,7 @@ import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -82,11 +76,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(RobolectricTestRunner.class) -@Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.powermock.*", - "com.squareup.okhttp", "okio.*", "javax.net.ssl.*" }) -@PrepareForTest({OkHttpTLS12Compat.class}) public class UsersAPIClientTest { private static final String CLIENT_ID = "CLIENTID"; @@ -110,9 +99,6 @@ public class UsersAPIClientTest { private UsersAPI mockAPI; - @Rule - public PowerMockRule rule = new PowerMockRule(); - @Before public void setUp() throws Exception { mockAPI = new UsersAPI(); @@ -132,7 +118,8 @@ public void shouldSetUserAgent() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); - final UsersAPIClient client = new UsersAPIClient(account, factory, okClient); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + final UsersAPIClient client = new UsersAPIClient(account, factory, okClient, tlsCompat); client.setUserAgent("android-user-agent"); verify(factory).setUserAgent("android-user-agent"); } @@ -143,9 +130,10 @@ public void shouldSetTelemetryIfPresent() throws Exception { when(telemetry.getValue()).thenReturn("the-telemetry-data"); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.setTelemetry(telemetry); - new UsersAPIClient(auth0, factory, okClient); + new UsersAPIClient(auth0, factory, okClient, tlsCompat); verify(factory).setClientInfo("the-telemetry-data"); } @@ -153,9 +141,10 @@ public void shouldSetTelemetryIfPresent() throws Exception { public void shouldNotSetTelemetryIfMissing() throws Exception { RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.doNotSendTelemetry(); - new UsersAPIClient(auth0, factory, okClient); + new UsersAPIClient(auth0, factory, okClient, tlsCompat); verify(factory, never()).setClientInfo(any(String.class)); } @@ -166,11 +155,12 @@ public void shouldEnableHttpLogging() throws Exception { when(account.isLoggingEnabled()).thenReturn(true); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); ArgumentCaptor interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class); - new UsersAPIClient(account, factory, okClient); + new UsersAPIClient(account, factory, okClient, tlsCompat); verify(okClient).interceptors(); verify(list).add(interceptorCaptor.capture()); @@ -187,10 +177,11 @@ public void shouldDisableHttpLogging() throws Exception { when(account.isLoggingEnabled()).thenReturn(false); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); - new UsersAPIClient(account, factory, okClient); + new UsersAPIClient(account, factory, okClient, tlsCompat); verify(okClient, never()).interceptors(); verify(list, never()).add(any(Interceptor.class)); @@ -202,10 +193,11 @@ public void shouldHaveHttpLoggingDisabledByDefault() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); List list = mock(List.class); when(okClient.interceptors()).thenReturn(list); - new UsersAPIClient(account, factory, okClient); + new UsersAPIClient(account, factory, okClient, tlsCompat); verify(okClient, never()).interceptors(); verify(list, never()).add(any(Interceptor.class)); @@ -414,9 +406,17 @@ public void shouldGetUserProfileSync() throws Exception { @Test public void shouldExtendTls12Support() { - PowerMockito.mockStatic(OkHttpTLS12Compat.class); + Auth0 account = mock(Auth0.class); + RequestFactory factory = mock(RequestFactory.class); + OkHttpClient okClient = mock(OkHttpClient.class); + OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + when(tlsCompat.setClient((OkHttpClient) anyObject())).thenReturn(tlsCompat); + UsersAPIClient client = new UsersAPIClient(account, factory, okClient, tlsCompat); + client.enableTLS12OnPreLollipop(); - PowerMockito.verifyStatic(); + + verify(tlsCompat).setClient(eq(client.client)); + verify(tlsCompat).enableForClient(); } private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { diff --git a/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java b/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java index bb0dee037..bf3eb7498 100644 --- a/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java +++ b/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java @@ -29,26 +29,28 @@ @Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) public class OkHttpTLS12CompatTest { - Activity activity; - @Mock OkHttpClient client; + private Activity activity; + private OkHttpTLS12Compat tlsCompat; + @Mock private OkHttpClient client; @Before public void setUp(){ MockitoAnnotations.initMocks(this); + tlsCompat = new OkHttpTLS12Compat().setClient(client); activity = Robolectric.setupActivity(Activity.class); } @Test @Config(sdk=22) public void shouldNotConfigTlsPostApi21() { - OkHttpTLS12Compat.enableForClient(client); + tlsCompat.enableForClient(); verify(client, never()).setSslSocketFactory((SSLSocketFactory) any()); } @Test @Config(sdk=21) public void shouldConfigTlsOnOrPreApi21() { - OkHttpTLS12Compat.enableForClient(client); + tlsCompat.enableForClient(); ArgumentCaptor factoryCaptor = ArgumentCaptor.forClass(SSLSocketFactory.class); verify(client).setSslSocketFactory(factoryCaptor.capture()); From c931684a1e4bf113e5f2783efbb3b75ca0f87df0 Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Mon, 13 Nov 2017 14:51:13 +0800 Subject: [PATCH 6/7] Refactor OkHttpTLS12Compat to OkHttpClientFactory. * Create and modify OkHttpClient instance in the factory. * Get / set preference on enforcing TLS 1.2 in Auth0. * Remove compat logic in API clients. * Test TLS / logging in factory instead of API clients. --- .../main/java/com/auth0/android/Auth0.java | 18 ++ .../AuthenticationAPIClient.java | 23 +-- .../android/management/UsersAPIClient.java | 22 +-- .../internal/OkHttpClientFactory.java} | 45 +++-- .../internal}/TLS12SocketFactory.java | 2 +- .../java/com/auth0/android/Auth0Test.java | 22 +++ .../AuthenticationAPIClientTest.java | 94 +-------- .../management/UsersAPIClientTest.java | 94 +-------- .../internal/OkHttpClientFactoryTest.java | 180 ++++++++++++++++++ .../internal}/TLS12SocketFactoryTest.java | 2 +- .../android/util/OkHttpTLS12CompatTest.java | 78 -------- 11 files changed, 280 insertions(+), 300 deletions(-) rename auth0/src/main/java/com/auth0/android/{util/OkHttpTLS12Compat.java => request/internal/OkHttpClientFactory.java} (53%) rename auth0/src/main/java/com/auth0/android/{util => request/internal}/TLS12SocketFactory.java (97%) create mode 100644 auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java rename auth0/src/test/java/com/auth0/android/{util => request/internal}/TLS12SocketFactoryTest.java (99%) delete mode 100644 auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java diff --git a/auth0/src/main/java/com/auth0/android/Auth0.java b/auth0/src/main/java/com/auth0/android/Auth0.java index d32932d2a..04865a5e2 100755 --- a/auth0/src/main/java/com/auth0/android/Auth0.java +++ b/auth0/src/main/java/com/auth0/android/Auth0.java @@ -61,6 +61,7 @@ public class Auth0 { private Telemetry telemetry; private boolean oidcConformant; private boolean loggingEnabled; + private boolean tls12Enforced; /** * Creates a new Auth0 instance with the 'com_auth0_client_id' and 'com_auth0_domain' values @@ -204,6 +205,23 @@ public void setLoggingEnabled(boolean enabled) { loggingEnabled = enabled; } + /** + * Getter for whether TLS 1.2 is enforced on devices with API 16-21. + * + * @return whether TLS 1.2 is enforced on devices with API 16-21. + */ + public boolean isTLS12Enforced() { + return tls12Enforced; + } + + /** + * Set whether to enforce TLS 1.2 on devices with API 16-21. + * @param enforced whether TLS 1.2 is enforced on devices with API 16-21. + */ + public void setTLS12Enforced(boolean enforced) { + tls12Enforced = enforced; + } + private HttpUrl resolveConfiguration(@Nullable String configurationDomain, @NonNull HttpUrl domainUrl) { HttpUrl url = ensureValidUrl(configurationDomain); if (url == null) { diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java index 090a4f349..9f065403e 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.java @@ -45,12 +45,11 @@ import com.auth0.android.result.DatabaseUser; import com.auth0.android.result.Delegation; import com.auth0.android.result.UserProfile; -import com.auth0.android.util.OkHttpTLS12Compat; +import com.auth0.android.request.internal.OkHttpClientFactory; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.logging.HttpLoggingInterceptor; import java.util.Map; @@ -99,7 +98,6 @@ public class AuthenticationAPIClient { private final Auth0 auth0; @VisibleForTesting final OkHttpClient client; - private final OkHttpTLS12Compat tlsCompat; private final Gson gson; private final RequestFactory factory; private final ErrorBuilder authErrorBuilder; @@ -111,7 +109,7 @@ public class AuthenticationAPIClient { * @param auth0 account information */ public AuthenticationAPIClient(@NonNull Auth0 auth0) { - this(auth0, new RequestFactory(), new OkHttpClient(), new OkHttpTLS12Compat(), GsonProvider.buildGson()); + this(auth0, new RequestFactory(), new OkHttpClientFactory(), GsonProvider.buildGson()); } /** @@ -125,17 +123,13 @@ public AuthenticationAPIClient(Context context) { } @VisibleForTesting - AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat) { - this(auth0, factory, client, tlsCompat, GsonProvider.buildGson()); + AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClientFactory clientFactory) { + this(auth0, factory, clientFactory, GsonProvider.buildGson()); } - private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat, Gson gson) { + private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClientFactory clientFactory, Gson gson) { this.auth0 = auth0; - this.client = client; - this.tlsCompat = tlsCompat; - if (auth0.isLoggingEnabled()) { - this.client.interceptors().add(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)); - } + this.client = clientFactory.createClient(auth0.isLoggingEnabled(), auth0.isTLS12Enforced()); this.gson = gson; this.factory = factory; this.authErrorBuilder = new AuthenticationErrorBuilder(); @@ -163,11 +157,6 @@ public void setUserAgent(String userAgent) { factory.setUserAgent(userAgent); } - @SuppressWarnings("unused") - public void enableTLS12OnPreLollipop() { - tlsCompat.setClient(client).enableForClient(); - } - /** * Log in a user with email/username and password for a connection/realm. * In OIDC conformant mode ({@link Auth0#isOIDCConformant()}) it will use the password-realm grant type for the {@code /oauth/token} endpoint diff --git a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java index 3674b8c42..6ca2517f4 100755 --- a/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java +++ b/auth0/src/main/java/com/auth0/android/management/UsersAPIClient.java @@ -37,7 +37,7 @@ import com.auth0.android.request.internal.RequestFactory; import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; -import com.auth0.android.util.OkHttpTLS12Compat; +import com.auth0.android.request.internal.OkHttpClientFactory; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -69,7 +69,6 @@ public class UsersAPIClient { private static final String USER_METADATA_KEY = "user_metadata"; private final Auth0 auth0; - private final OkHttpTLS12Compat tlsCompat; @VisibleForTesting final OkHttpClient client; private final Gson gson; private final RequestFactory factory; @@ -82,7 +81,7 @@ public class UsersAPIClient { * @param token of the primary identity */ public UsersAPIClient(Auth0 auth0, String token) { - this(auth0, new RequestFactory(token), new OkHttpClient(), new OkHttpTLS12Compat(), GsonProvider.buildGson()); + this(auth0, new RequestFactory(token), new OkHttpClientFactory(), GsonProvider.buildGson()); } /** @@ -97,17 +96,13 @@ public UsersAPIClient(Context context, String token) { } @VisibleForTesting - UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat) { - this(auth0, factory, client, tlsCompat, GsonProvider.buildGson()); + UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClientFactory clientFactory) { + this(auth0, factory, clientFactory, GsonProvider.buildGson()); } - private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, OkHttpTLS12Compat tlsCompat, Gson gson) { + private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClientFactory clientFactory, Gson gson) { this.auth0 = auth0; - this.client = client; - this.tlsCompat = tlsCompat; - if (auth0.isLoggingEnabled()) { - this.client.interceptors().add(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)); - } + client = clientFactory.createClient(auth0.isLoggingEnabled(), auth0.isTLS12Enforced()); this.gson = gson; this.factory = factory; this.mgmtErrorBuilder = new ManagementErrorBuilder(); @@ -135,11 +130,6 @@ public void setUserAgent(String userAgent) { factory.setUserAgent(userAgent); } - @SuppressWarnings("Unused") - public void enableTLS12OnPreLollipop() { - tlsCompat.setClient(client).enableForClient(); - } - /** * Link a user identity calling '/api/v2/users/:primaryUserId/identities' endpoint * Example usage: diff --git a/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java b/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java similarity index 53% rename from auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java rename to auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java index bbaea8f50..936e00f19 100644 --- a/auth0/src/main/java/com/auth0/android/util/OkHttpTLS12Compat.java +++ b/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java @@ -1,11 +1,14 @@ -package com.auth0.android.util; +package com.auth0.android.request.internal; import android.os.Build; +import android.support.annotation.VisibleForTesting; import android.util.Log; import com.squareup.okhttp.ConnectionSpec; +import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.TlsVersion; +import com.squareup.okhttp.logging.HttpLoggingInterceptor; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -14,19 +17,36 @@ import javax.net.ssl.SSLContext; -public class OkHttpTLS12Compat { +public class OkHttpClientFactory { - private static final String TAG = OkHttpTLS12Compat.class.getSimpleName(); - - private OkHttpClient client = null; + private static final String TAG = OkHttpClientFactory.class.getSimpleName(); /** - * Sets the OkHttp client instance - * @param client OkHttpClient instance to be modified + * This method creates an instance of OKHttpClient according to the provided parameters. + * It is used internally and is not intended to be used directly. + * @param loggingEnabled Enable logging in the created OkHttpClient. + * @param tls12Enforced Enforce TLS 1.2 in the created OkHttpClient on devices with API 16-21 + * @return new OkHttpClient instance created according to the parameters. */ - public OkHttpTLS12Compat setClient(OkHttpClient client) { - this.client = client; - return this; + public OkHttpClient createClient(boolean loggingEnabled, boolean tls12Enforced) { + return modifyClient(new OkHttpClient(), loggingEnabled, tls12Enforced); + } + + @VisibleForTesting + OkHttpClient modifyClient(OkHttpClient client, boolean loggingEnabled, boolean tls12Enforced) { + if (loggingEnabled) { + enableLogging(client); + } + if (tls12Enforced) { + enforceTls12(client); + } + return client; + } + + private void enableLogging(OkHttpClient client) { + Interceptor interceptor = new HttpLoggingInterceptor() + .setLevel(HttpLoggingInterceptor.Level.BODY); + client.interceptors().add(interceptor); } /** @@ -34,12 +54,12 @@ public OkHttpTLS12Compat setClient(OkHttpClient client) { * @link https://github.com/square/okhttp/issues/2372 * @see TLS12SocketFactory */ - public OkHttpTLS12Compat enableForClient() { + private void enforceTls12(OkHttpClient client) { // No need to modify client as TLS 1.2 is enabled by default on API21+ // Lollipop is included because some Samsung devices face the same problem on API 21. if (client == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN || Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - return this; + return; } try { SSLContext sc = SSLContext.getInstance("TLSv1.2"); @@ -59,6 +79,5 @@ public OkHttpTLS12Compat enableForClient() { } catch (NoSuchAlgorithmException | KeyManagementException e) { Log.e(TAG, "Error while setting TLS 1.2", e); } - return this; } } diff --git a/auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java b/auth0/src/main/java/com/auth0/android/request/internal/TLS12SocketFactory.java similarity index 97% rename from auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java rename to auth0/src/main/java/com/auth0/android/request/internal/TLS12SocketFactory.java index 1c0e0a05d..e5cf385c9 100644 --- a/auth0/src/main/java/com/auth0/android/util/TLS12SocketFactory.java +++ b/auth0/src/main/java/com/auth0/android/request/internal/TLS12SocketFactory.java @@ -1,4 +1,4 @@ -package com.auth0.android.util; +package com.auth0.android.request.internal; import android.support.annotation.VisibleForTesting; diff --git a/auth0/src/test/java/com/auth0/android/Auth0Test.java b/auth0/src/test/java/com/auth0/android/Auth0Test.java index 7ba39cb95..9cc9e3471 100755 --- a/auth0/src/test/java/com/auth0/android/Auth0Test.java +++ b/auth0/src/test/java/com/auth0/android/Auth0Test.java @@ -107,6 +107,28 @@ public void shouldNotHaveLoggingEnabled() throws Exception { assertThat(auth0.isLoggingEnabled(), is(false)); } + @Test + public void shouldNotEnforceTLS12ByDefault() throws Exception { + Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); + assertThat(auth0.isTLS12Enforced(), is(false)); + } + + @Test + public void shouldHaveTLS12Enforced() throws Exception { + Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); + auth0.setTLS12Enforced(true); + + assertThat(auth0.isTLS12Enforced(), is(true)); + } + + @Test + public void shouldNotHaveTLS12Enforced() throws Exception { + Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); + auth0.setTLS12Enforced(false); + + assertThat(auth0.isTLS12Enforced(), is(false)); + } + @Test public void shouldNotHaveLoggingEnabledByDefault() throws Exception { Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java index 832742a04..7e9f69250 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import com.auth0.android.Auth0; +import com.auth0.android.request.internal.OkHttpClientFactory; import com.auth0.android.request.internal.RequestFactory; import com.auth0.android.result.Authentication; import com.auth0.android.result.Credentials; @@ -37,29 +38,23 @@ import com.auth0.android.result.UserProfile; import com.auth0.android.util.AuthenticationAPI; import com.auth0.android.util.MockAuthenticationCallback; -import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.squareup.okhttp.HttpUrl; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.logging.HttpLoggingInterceptor; import com.squareup.okhttp.mockwebserver.RecordedRequest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.lang.reflect.Type; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; @@ -73,13 +68,11 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -126,9 +119,8 @@ public void tearDown() throws Exception { public void shouldSetUserAgent() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient, tlsCompat); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); + AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, clientFactory); client.setUserAgent("nexus-5x"); verify(factory).setUserAgent("nexus-5x"); } @@ -138,80 +130,23 @@ public void shouldSetTelemetryIfPresent() throws Exception { final Telemetry telemetry = mock(Telemetry.class); when(telemetry.getValue()).thenReturn("the-telemetry-data"); RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.setTelemetry(telemetry); - AuthenticationAPIClient client = new AuthenticationAPIClient(auth0, factory, okClient, tlsCompat); + AuthenticationAPIClient client = new AuthenticationAPIClient(auth0, factory, clientFactory); verify(factory).setClientInfo("the-telemetry-data"); } @Test public void shouldNotSetTelemetryIfMissing() throws Exception { RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.doNotSendTelemetry(); - new AuthenticationAPIClient(auth0, factory, okClient, tlsCompat); + AuthenticationAPIClient client = new AuthenticationAPIClient(auth0, factory, clientFactory); verify(factory, never()).setClientInfo(any(String.class)); } - @SuppressWarnings("unchecked") - @Test - public void shouldEnableHttpLogging() throws Exception { - Auth0 account = mock(Auth0.class); - when(account.isLoggingEnabled()).thenReturn(true); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - ArgumentCaptor interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class); - new AuthenticationAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient).interceptors(); - verify(list).add(interceptorCaptor.capture()); - - assertThat(interceptorCaptor.getValue(), is(notNullValue())); - assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class))); - assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.BODY)); - } - - @SuppressWarnings("unchecked") - @Test - public void shouldDisableHttpLogging() throws Exception { - Auth0 account = mock(Auth0.class); - when(account.isLoggingEnabled()).thenReturn(false); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - new AuthenticationAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient, never()).interceptors(); - verify(list, never()).add(any(Interceptor.class)); - } - - @SuppressWarnings("unchecked") - @Test - public void shouldHaveHttpLoggingDisabledByDefault() throws Exception { - Auth0 account = mock(Auth0.class); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - new AuthenticationAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient, never()).interceptors(); - verify(list, never()).add(any(Interceptor.class)); - } - @Test public void shouldCreateClientWithAccountInfo() throws Exception { AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0(CLIENT_ID, DOMAIN)); @@ -1782,21 +1717,6 @@ public void shouldParseUnauthorizedPKCEError() throws Exception { assertThat(callback.getError().getDescription(), is(equalTo("Unauthorized"))); } - @Test - public void shouldExtendTls12Support() { - Auth0 account = mock(Auth0.class); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - when(tlsCompat.setClient((OkHttpClient) anyObject())).thenReturn(tlsCompat); - AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient, tlsCompat); - - client.enableTLS12OnPreLollipop(); - - verify(tlsCompat).setClient(eq(client.client)); - verify(tlsCompat).enableForClient(); - } - private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { final Type mapType = new TypeToken>() { }.getType(); diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java index e126d5d58..736b4899d 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.java @@ -29,27 +29,22 @@ import android.content.res.Resources; import com.auth0.android.Auth0; +import com.auth0.android.request.internal.OkHttpClientFactory; import com.auth0.android.request.internal.RequestFactory; import com.auth0.android.result.UserIdentity; import com.auth0.android.result.UserProfile; import com.auth0.android.util.MockManagementCallback; -import com.auth0.android.util.OkHttpTLS12Compat; import com.auth0.android.util.Telemetry; import com.auth0.android.util.TypeTokenMatcher; import com.auth0.android.util.UsersAPI; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.logging.HttpLoggingInterceptor; import com.squareup.okhttp.mockwebserver.RecordedRequest; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.powermock.api.mockito.PowerMockito; import java.lang.reflect.Type; import java.util.Arrays; @@ -61,14 +56,12 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isA; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -117,9 +110,8 @@ public void tearDown() throws Exception { public void shouldSetUserAgent() throws Exception { Auth0 account = mock(Auth0.class); RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - final UsersAPIClient client = new UsersAPIClient(account, factory, okClient, tlsCompat); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); + final UsersAPIClient client = new UsersAPIClient(account, factory, clientFactory); client.setUserAgent("android-user-agent"); verify(factory).setUserAgent("android-user-agent"); } @@ -129,80 +121,23 @@ public void shouldSetTelemetryIfPresent() throws Exception { final Telemetry telemetry = mock(Telemetry.class); when(telemetry.getValue()).thenReturn("the-telemetry-data"); RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.setTelemetry(telemetry); - new UsersAPIClient(auth0, factory, okClient, tlsCompat); + new UsersAPIClient(auth0, factory, clientFactory); verify(factory).setClientInfo("the-telemetry-data"); } @Test public void shouldNotSetTelemetryIfMissing() throws Exception { RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); + OkHttpClientFactory clientFactory = mock(OkHttpClientFactory.class); Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN); auth0.doNotSendTelemetry(); - new UsersAPIClient(auth0, factory, okClient, tlsCompat); + new UsersAPIClient(auth0, factory, clientFactory); verify(factory, never()).setClientInfo(any(String.class)); } - @SuppressWarnings("unchecked") - @Test - public void shouldEnableHttpLogging() throws Exception { - Auth0 account = mock(Auth0.class); - when(account.isLoggingEnabled()).thenReturn(true); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - ArgumentCaptor interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class); - new UsersAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient).interceptors(); - verify(list).add(interceptorCaptor.capture()); - - assertThat(interceptorCaptor.getValue(), is(notNullValue())); - assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class))); - assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.BODY)); - } - - @SuppressWarnings("unchecked") - @Test - public void shouldDisableHttpLogging() throws Exception { - Auth0 account = mock(Auth0.class); - when(account.isLoggingEnabled()).thenReturn(false); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - new UsersAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient, never()).interceptors(); - verify(list, never()).add(any(Interceptor.class)); - } - - @SuppressWarnings("unchecked") - @Test - public void shouldHaveHttpLoggingDisabledByDefault() throws Exception { - Auth0 account = mock(Auth0.class); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - List list = mock(List.class); - when(okClient.interceptors()).thenReturn(list); - - new UsersAPIClient(account, factory, okClient, tlsCompat); - - verify(okClient, never()).interceptors(); - verify(list, never()).add(any(Interceptor.class)); - } - @Test public void shouldCreateClientWithAccountInfo() throws Exception { UsersAPIClient client = new UsersAPIClient(new Auth0(CLIENT_ID, DOMAIN), TOKEN_PRIMARY); @@ -404,21 +339,6 @@ public void shouldGetUserProfileSync() throws Exception { assertThat(result, isA(UserProfile.class)); } - @Test - public void shouldExtendTls12Support() { - Auth0 account = mock(Auth0.class); - RequestFactory factory = mock(RequestFactory.class); - OkHttpClient okClient = mock(OkHttpClient.class); - OkHttpTLS12Compat tlsCompat = mock(OkHttpTLS12Compat.class); - when(tlsCompat.setClient((OkHttpClient) anyObject())).thenReturn(tlsCompat); - UsersAPIClient client = new UsersAPIClient(account, factory, okClient, tlsCompat); - - client.enableTLS12OnPreLollipop(); - - verify(tlsCompat).setClient(eq(client.client)); - verify(tlsCompat).enableForClient(); - } - private Map bodyFromRequest(RecordedRequest request) throws java.io.IOException { final Type mapType = new TypeToken>() { }.getType(); diff --git a/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java b/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java new file mode 100644 index 000000000..dced394f5 --- /dev/null +++ b/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java @@ -0,0 +1,180 @@ +package com.auth0.android.request.internal; + +import android.app.Activity; + +import com.squareup.okhttp.ConnectionSpec; +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.TlsVersion; +import com.squareup.okhttp.logging.HttpLoggingInterceptor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.List; + +import javax.net.ssl.SSLSocketFactory; + +import static junit.framework.Assert.assertTrue; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +@Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) +public class OkHttpClientFactoryTest { + + private Activity activity; + private OkHttpClientFactory factory; + @Mock private OkHttpClient mockClient; + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + factory = new OkHttpClientFactory(); + activity = Robolectric.setupActivity(Activity.class); + } + + @Test + // Verify that there's no error when creating a new OkHttpClient instance + public void shouldCreateNewClient(){ + factory.createClient(false, false); + } + + @Test + @Config(sdk=21) + public void shouldEnableLoggingTLS12Enforced() { + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, true, true); + verifyLoggingEnabled(client, list); + verifyTLS12Enforced(client); + } + + @Test + @Config(sdk=21) + public void shouldEnableLoggingTLS12NotEnforced(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, true, false); + verifyLoggingEnabled(client, list); + verifyTLS12NotEnforced(client); + } + + @Test + @Config(sdk=21) + public void shouldDisableLoggingTLS12Enforced(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, false, true); + verifyLoggingDisabled(client, list); + verifyTLS12Enforced(client); + } + + @Test + @Config(sdk=21) + public void shouldDisableLoggingTLS12NotEnforced(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, false, false); + verifyLoggingDisabled(client, list); + verifyTLS12NotEnforced(client); + } + + @Test + @Config(sdk=22) + public void shouldEnableLoggingTLS12Enforced_postLollipopTLS12NoEffect() { + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, true, true); + verifyLoggingEnabled(client, list); + verifyTLS12NotEnforced(client); + } + + @Test + @Config(sdk=22) + public void shouldEnableLoggingTLS12NotEnforced_posLollipop(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, true, false); + verifyLoggingEnabled(client, list); + verifyTLS12NotEnforced(client); + } + + @Test + @Config(sdk=22) + public void shouldDisableLoggingTLS12Enforced_postLollipopTLS12NoEffect(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, false, true); + verifyLoggingDisabled(client, list); + verifyTLS12NotEnforced(client); + } + + @Test + @Config(sdk=22) + public void shouldDisableLoggingTLS12NotEnforced_postLollipop(){ + List list = generateMockList(mockClient); + OkHttpClient client = factory.modifyClient(mockClient, false, false); + verifyLoggingDisabled(client, list); + verifyTLS12NotEnforced(client); + } + + private static List generateMockList(OkHttpClient client) { + List list = mock(List.class); + when(client.interceptors()).thenReturn(list); + return list; + } + + private static void verifyLoggingEnabled(OkHttpClient client, List list) { + verify(client).interceptors(); + + ArgumentCaptor interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class); + verify(list).add(interceptorCaptor.capture()); + + assertThat(interceptorCaptor.getValue(), is(notNullValue())); + assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class))); + assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.BODY)); + } + + private static void verifyLoggingDisabled(OkHttpClient client, List list) { + verify(client, never()).interceptors(); + verify(list, never()).add(any(Interceptor.class)); + } + + private static void verifyTLS12NotEnforced(OkHttpClient client) { + verify(client, never()).setSslSocketFactory((SSLSocketFactory) any()); + } + + private static void verifyTLS12Enforced(OkHttpClient client) { + + ArgumentCaptor factoryCaptor = ArgumentCaptor.forClass(SSLSocketFactory.class); + verify(client).setSslSocketFactory(factoryCaptor.capture()); + assertTrue(factoryCaptor.getValue() instanceof TLS12SocketFactory); + + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(List.class); + verify(client).setConnectionSpecs(specCaptor.capture()); + boolean hasTls12 = false; + for (Object item : specCaptor.getValue()) { + assertTrue(item instanceof ConnectionSpec); + ConnectionSpec spec = (ConnectionSpec) item; + if (!spec.isTls()) { + continue; + } + List versions = spec.tlsVersions(); + for (TlsVersion version : versions) { + if ("TLSv1.2".equals(version.javaName())) { + hasTls12 = true; + break; + } + } + } + assertTrue(hasTls12); + } +} diff --git a/auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java b/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java similarity index 99% rename from auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java rename to auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java index caffa5de1..c01cfdf21 100644 --- a/auth0/src/test/java/com/auth0/android/util/TLS12SocketFactoryTest.java +++ b/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java @@ -1,4 +1,4 @@ -package com.auth0.android.util; +package com.auth0.android.request.internal; import org.junit.Before; import org.junit.Rule; diff --git a/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java b/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java deleted file mode 100644 index bf3eb7498..000000000 --- a/auth0/src/test/java/com/auth0/android/util/OkHttpTLS12CompatTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.auth0.android.util; - -import android.app.Activity; - -import com.squareup.okhttp.ConnectionSpec; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.TlsVersion; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -import java.util.List; - -import javax.net.ssl.SSLSocketFactory; - -import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -@RunWith(RobolectricTestRunner.class) -@Config(constants = com.auth0.android.auth0.BuildConfig.class, sdk = 21, manifest = Config.NONE) -public class OkHttpTLS12CompatTest { - - private Activity activity; - private OkHttpTLS12Compat tlsCompat; - @Mock private OkHttpClient client; - - @Before - public void setUp(){ - MockitoAnnotations.initMocks(this); - tlsCompat = new OkHttpTLS12Compat().setClient(client); - activity = Robolectric.setupActivity(Activity.class); - } - - @Test - @Config(sdk=22) - public void shouldNotConfigTlsPostApi21() { - tlsCompat.enableForClient(); - verify(client, never()).setSslSocketFactory((SSLSocketFactory) any()); - } - - @Test - @Config(sdk=21) - public void shouldConfigTlsOnOrPreApi21() { - tlsCompat.enableForClient(); - - ArgumentCaptor factoryCaptor = ArgumentCaptor.forClass(SSLSocketFactory.class); - verify(client).setSslSocketFactory(factoryCaptor.capture()); - assertTrue(factoryCaptor.getValue() instanceof TLS12SocketFactory); - - ArgumentCaptor specCaptor = ArgumentCaptor.forClass(List.class); - verify(client).setConnectionSpecs(specCaptor.capture()); - boolean hasTls12 = false; - for (Object item : specCaptor.getValue()) { - assertTrue(item instanceof ConnectionSpec); - ConnectionSpec spec = (ConnectionSpec) item; - if (!spec.isTls()) { - continue; - } - List versions = spec.tlsVersions(); - for (TlsVersion version : versions) { - if ("TLSv1.2".equals(version.javaName())) { - hasTls12 = true; - break; - } - } - } - assertTrue(hasTls12); - } -} From 318b95ce06342d97c63ff30075bdf9ad3ce528a3 Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Tue, 14 Nov 2017 09:47:46 +0800 Subject: [PATCH 7/7] Clean up OkHttpClientFactory. --- .../request/internal/OkHttpClientFactory.java | 7 ++++++- .../internal/OkHttpClientFactoryTest.java | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java b/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java index 936e00f19..78696712a 100644 --- a/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java +++ b/auth0/src/main/java/com/auth0/android/request/internal/OkHttpClientFactory.java @@ -17,6 +17,11 @@ import javax.net.ssl.SSLContext; +/** + * Factory class used to configure and obtain a new OkHttpClient instance. + * This class is meant for internal use only, + * breaking changes may appear at any time without backwards compatibility guarantee. + */ public class OkHttpClientFactory { private static final String TAG = OkHttpClientFactory.class.getSimpleName(); @@ -57,7 +62,7 @@ private void enableLogging(OkHttpClient client) { private void enforceTls12(OkHttpClient client) { // No need to modify client as TLS 1.2 is enabled by default on API21+ // Lollipop is included because some Samsung devices face the same problem on API 21. - if (client == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN || Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { return; } diff --git a/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java b/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java index dced394f5..275437612 100644 --- a/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java +++ b/auth0/src/test/java/com/auth0/android/request/internal/OkHttpClientFactoryTest.java @@ -57,7 +57,7 @@ public void shouldCreateNewClient(){ @Test @Config(sdk=21) public void shouldEnableLoggingTLS12Enforced() { - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, true, true); verifyLoggingEnabled(client, list); verifyTLS12Enforced(client); @@ -66,7 +66,7 @@ public void shouldEnableLoggingTLS12Enforced() { @Test @Config(sdk=21) public void shouldEnableLoggingTLS12NotEnforced(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, true, false); verifyLoggingEnabled(client, list); verifyTLS12NotEnforced(client); @@ -75,7 +75,7 @@ public void shouldEnableLoggingTLS12NotEnforced(){ @Test @Config(sdk=21) public void shouldDisableLoggingTLS12Enforced(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, false, true); verifyLoggingDisabled(client, list); verifyTLS12Enforced(client); @@ -84,7 +84,7 @@ public void shouldDisableLoggingTLS12Enforced(){ @Test @Config(sdk=21) public void shouldDisableLoggingTLS12NotEnforced(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, false, false); verifyLoggingDisabled(client, list); verifyTLS12NotEnforced(client); @@ -93,7 +93,7 @@ public void shouldDisableLoggingTLS12NotEnforced(){ @Test @Config(sdk=22) public void shouldEnableLoggingTLS12Enforced_postLollipopTLS12NoEffect() { - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, true, true); verifyLoggingEnabled(client, list); verifyTLS12NotEnforced(client); @@ -102,7 +102,7 @@ public void shouldEnableLoggingTLS12Enforced_postLollipopTLS12NoEffect() { @Test @Config(sdk=22) public void shouldEnableLoggingTLS12NotEnforced_posLollipop(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, true, false); verifyLoggingEnabled(client, list); verifyTLS12NotEnforced(client); @@ -111,7 +111,7 @@ public void shouldEnableLoggingTLS12NotEnforced_posLollipop(){ @Test @Config(sdk=22) public void shouldDisableLoggingTLS12Enforced_postLollipopTLS12NoEffect(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, false, true); verifyLoggingDisabled(client, list); verifyTLS12NotEnforced(client); @@ -120,13 +120,13 @@ public void shouldDisableLoggingTLS12Enforced_postLollipopTLS12NoEffect(){ @Test @Config(sdk=22) public void shouldDisableLoggingTLS12NotEnforced_postLollipop(){ - List list = generateMockList(mockClient); + List list = generateInterceptorsMockList(mockClient); OkHttpClient client = factory.modifyClient(mockClient, false, false); verifyLoggingDisabled(client, list); verifyTLS12NotEnforced(client); } - private static List generateMockList(OkHttpClient client) { + private static List generateInterceptorsMockList(OkHttpClient client) { List list = mock(List.class); when(client.interceptors()).thenReturn(list); return list;