From cb76ba558478a6268a4dd092a25e8dd3a8347d92 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:13:18 -0700 Subject: [PATCH 1/2] [local_auth] Fix device credential only check for API < 30 (#6522) --- .../local_auth_android/CHANGELOG.md | 4 ++ .../local_auth_android/android/build.gradle | 1 + .../plugins/localauth/LocalAuthPlugin.java | 22 ++++++++- .../plugins/localauth/LocalAuthTest.java | 47 ++++++++++++++++++- .../local_auth_android/pubspec.yaml | 2 +- 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index a26846d5ed01..c9eeed9d01dd 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.14 + +* Fixes device credential authentication for API versions before R. + ## 1.0.13 * Updates imports for `prefer_relative_imports`. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 569d7e3564b4..6c9417008d27 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -56,6 +56,7 @@ dependencies { api "androidx.fragment:fragment:1.5.2" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'org.robolectric:robolectric:4.5' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index e8632c474030..e545df01e7c0 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -253,11 +253,16 @@ public ArrayList getEnrolledBiometrics() { } @VisibleForTesting - public boolean isDeviceSupported() { + public boolean isDeviceSecure() { if (keyguardManager == null) return false; return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager.isDeviceSecure()); } + @VisibleForTesting + public boolean isDeviceSupported() { + return isDeviceSecure() || canAuthenticateWithBiometrics(); + } + private boolean canAuthenticateWithBiometrics() { if (biometricManager == null) return false; return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) @@ -270,7 +275,15 @@ private boolean hasBiometricHardware() { != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; } - private boolean canAuthenticateWithDeviceCredential() { + @VisibleForTesting + public boolean canAuthenticateWithDeviceCredential() { + if (Build.VERSION.SDK_INT < 30) { + // Checking for device credential only authentication via the BiometricManager + // is not allowed before API level 30, so we check for presence of PIN, pattern, + // or password instead. + return isDeviceSecure(); + } + if (biometricManager == null) return false; return biometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS; @@ -334,4 +347,9 @@ final Activity getActivity() { void setBiometricManager(BiometricManager biometricManager) { this.biometricManager = biometricManager; } + + @VisibleForTesting + void setKeyguardManager(KeyguardManager keyguardManager) { + this.keyguardManager = keyguardManager; + } } diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 0eaf31255317..7279a3c49af2 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.KeyguardManager; import android.app.NativeActivity; import android.content.Context; import androidx.biometric.BiometricManager; @@ -34,8 +35,12 @@ import java.util.Collections; import java.util.HashMap; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +@RunWith(RobolectricTestRunner.class) public class LocalAuthTest { @Test public void authenticate_returnsErrorWhenAuthInProgress() { @@ -107,14 +112,13 @@ public void authenticate_properlyConfiguresBiometricOnlyAuthenticationRequest() } @Test + @Config(sdk = 30) public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthenticationRequest() { final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); when(plugin.isDeviceSupported()).thenReturn(true); final BiometricManager mockBiometricManager = mock(BiometricManager.class); - when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) - .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); plugin.setBiometricManager(mockBiometricManager); @@ -135,6 +139,7 @@ public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthentic } @Test + @Config(sdk = 30) public void authenticate_properlyConfiguresDeviceCredentialOnlyAuthenticationRequest() { final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); @@ -343,6 +348,44 @@ public void getEnrolledBiometrics_shouldAddStrongBiometrics() { }); } + @Test + @Config(sdk = 22) + public void isDeviceSecure_returnsFalseOnBelowApi23() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + assertFalse(plugin.isDeviceSecure()); + } + + @Test + @Config(sdk = 23) + public void isDeviceSecure_returnsTrueIfDeviceIsSecure() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + KeyguardManager mockKeyguardManager = mock(KeyguardManager.class); + plugin.setKeyguardManager(mockKeyguardManager); + + when(mockKeyguardManager.isDeviceSecure()).thenReturn(true); + assertTrue(plugin.isDeviceSecure()); + + when(mockKeyguardManager.isDeviceSecure()).thenReturn(false); + assertFalse(plugin.isDeviceSecure()); + } + + @Test + @Config(sdk = 30) + public void + canAuthenticateWithDeviceCredential_returnsTrueIfHasBiometricManagerSupportAboveApi30() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + plugin.setBiometricManager(mockBiometricManager); + + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + assertTrue(plugin.canAuthenticateWithDeviceCredential()); + + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + assertFalse(plugin.canAuthenticateWithDeviceCredential()); + } + private Activity buildMockActivityWithContext(Activity mockActivity) { final Context mockContext = mock(Context.class); when(mockActivity.getBaseContext()).thenReturn(mockContext); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 35c2d3af983c..99e9e2c547cc 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.13 +version: 1.0.14 environment: sdk: ">=2.14.0 <3.0.0" From fed91041ddd9b44ac90015a3f0a1bc77e6404414 Mon Sep 17 00:00:00 2001 From: Julius Bredemeyer <48645716+IVLIVS-III@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:04:05 +0200 Subject: [PATCH 2/2] [in_app_purchase_storekit] Fixup iOS integration test placeholder and remove exclusion (#6604) --- .../example/test_driver/{test => }/integration_test.dart | 0 script/configs/exclude_integration_ios.yaml | 2 -- 2 files changed, 2 deletions(-) rename packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/{test => }/integration_test.dart (100%) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/test/integration_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/integration_test.dart similarity index 100% rename from packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/test/integration_test.dart rename to packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/integration_test.dart diff --git a/script/configs/exclude_integration_ios.yaml b/script/configs/exclude_integration_ios.yaml index 19dfdb0350dc..2d535cd4f0dc 100644 --- a/script/configs/exclude_integration_ios.yaml +++ b/script/configs/exclude_integration_ios.yaml @@ -1,5 +1,3 @@ -# Currently missing: https://github.com/flutter/flutter/issues/81695 -- in_app_purchase_storekit # Currently missing: https://github.com/flutter/flutter/issues/82208 - ios_platform_images # Can't use Flutter integration tests due to native modal UI.