diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 5c305c099f..0b92fc6ce4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -1388,7 +1388,9 @@ class AuthRepositoryImpl( * - Cannot have two-factor authentication enabled. */ private fun newDeviceNoticePreConditionsValid(): Boolean { - if (environmentRepository.environment.type == Environment.Type.SELF_HOSTED) { + val checkEnvironment = !featureFlagManager.getFeatureFlag(FlagKey.IgnoreEnvironmentCheck) + val isSelfHosted = environmentRepository.environment.type == Environment.Type.SELF_HOSTED + if (checkEnvironment && isSelfHosted) { return false } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt index 1b2bf1ea7b..0315863d95 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt @@ -38,6 +38,7 @@ sealed class FlagKey { AppReviewPrompt, NewDevicePermanentDismiss, NewDeviceTemporaryDismiss, + IgnoreEnvironmentCheck, ) } } @@ -161,6 +162,15 @@ sealed class FlagKey { override val isRemotelyConfigured: Boolean = true } + /** + * Data object holding the feature flag key to ignore an environment check. + */ + data object IgnoreEnvironmentCheck : FlagKey() { + override val keyName: String = "ignore-environment-check" + override val defaultValue: Boolean = false + override val isRemotelyConfigured: Boolean = false + } + //region Dummy keys for testing /** * Data object holding the key for a [Boolean] flag to be used in tests. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt index 3783148fe2..8e8f75ed67 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt @@ -35,6 +35,7 @@ fun FlagKey.ListItemContent( FlagKey.CipherKeyEncryption, FlagKey.NewDevicePermanentDismiss, FlagKey.NewDeviceTemporaryDismiss, + FlagKey.IgnoreEnvironmentCheck, -> BooleanFlagItem( label = flagKey.getDisplayLabel(), key = flagKey as FlagKey, @@ -85,4 +86,5 @@ private fun FlagKey.getDisplayLabel(): String = when (this) { FlagKey.CipherKeyEncryption -> stringResource(R.string.cipher_key_encryption) FlagKey.NewDevicePermanentDismiss -> stringResource(R.string.new_device_permanent_dismiss) FlagKey.NewDeviceTemporaryDismiss -> stringResource(R.string.new_device_temporary_dismiss) + FlagKey.IgnoreEnvironmentCheck -> stringResource(R.string.ignore_environment_check) } diff --git a/app/src/main/res/values/strings_non_localized.xml b/app/src/main/res/values/strings_non_localized.xml index 221fd54720..d9aa5fd516 100644 --- a/app/src/main/res/values/strings_non_localized.xml +++ b/app/src/main/res/values/strings_non_localized.xml @@ -24,5 +24,6 @@ Cipher Key Encryption"> New device notice permanent dismiss"> New device notice temporary dismiss"> + Ignore environment check"> diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index e2165e4015..295014c384 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -250,7 +250,10 @@ class AuthRepositoryTest { } private val featureFlagManager: FeatureFlagManager = mockk(relaxed = true) { + every { getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) } returns true + every { getFeatureFlag(FlagKey.NewDevicePermanentDismiss) } returns true every { getFeatureFlag(FlagKey.OnboardingFlow) } returns false + every { getFeatureFlag(FlagKey.IgnoreEnvironmentCheck) } returns false } private val firstTimeActionManager = mockk { @@ -6530,12 +6533,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice flags on, is cloud user, profile at least week old, no required sso policy, no two factor enable returns true`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6571,14 +6568,47 @@ class AuthRepositoryTest { } @Test - fun `checkUserNeedsNewDeviceTwoFactorNotice has required SSO policy returns false`() = + @Suppress("MaxLineLength") + fun `checkUserNeedsNewDeviceTwoFactorNotice IgnoreEnvironmentCheck flag enabled should not check for a cloud environment and return true`() = runTest { every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) + featureFlagManager.getFeatureFlag(FlagKey.IgnoreEnvironmentCheck) } returns true every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true + policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) + } returns listOf() + fakeEnvironmentRepository.environment = Environment.SelfHosted( + EnvironmentUrlDataJson(base = "https://myselfhosted.environment.com"), + ) + + fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 + + val shouldShowNewDeviceNotice = repository.checkUserNeedsNewDeviceTwoFactorNotice() + + assertTrue(shouldShowNewDeviceNotice) + } + + @Test + @Suppress("MaxLineLength") + fun `checkUserNeedsNewDeviceTwoFactorNotice if environment is selfhosted return false`() = + runTest { + every { + policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) + } returns listOf() + fakeEnvironmentRepository.environment = Environment.SelfHosted( + EnvironmentUrlDataJson(base = "https://myselfhosted.environment.com"), + ) + + fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 + + val shouldShowNewDeviceNotice = repository.checkUserNeedsNewDeviceTwoFactorNotice() + + assertFalse(shouldShowNewDeviceNotice) + } + + @Test + fun `checkUserNeedsNewDeviceTwoFactorNotice has required SSO policy returns false`() = + runTest { every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf( @@ -6599,12 +6629,6 @@ class AuthRepositoryTest { @Test fun `checkUserNeedsNewDeviceTwoFactorNotice with two factor enable returns false`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6620,12 +6644,6 @@ class AuthRepositoryTest { @Test fun `checkUserNeedsNewDeviceTwoFactorNotice account less than a week old returns false`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6652,12 +6670,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice with NewDeviceNoticeDisplayStatus CAN_ACCESS_EMAIL_PERMANENT return false`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6681,12 +6693,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice with NewDeviceNoticeDisplayStatus HAS_NOT_SEEN return true`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6710,12 +6716,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice with NewDeviceNoticeDisplayStatus HAS_SEEN return true if date is older than 7 days`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6739,12 +6739,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice with NewDeviceNoticeDisplayStatus HAS_SEEN return false if date is not older than 7 days`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6768,12 +6762,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice with NewDeviceNoticeDisplayStatus CAN_ACCESS_EMAIL return permanent flag value`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6800,12 +6788,6 @@ class AuthRepositoryTest { @Test fun `checkUserNeedsNewDeviceTwoFactorNotice with no active user returns false`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6819,12 +6801,6 @@ class AuthRepositoryTest { @Test fun `checkUserNeedsNewDeviceTwoFactorNotice account with null creationDate returns false`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() @@ -6848,12 +6824,6 @@ class AuthRepositoryTest { @Suppress("MaxLineLength") fun `checkUserNeedsNewDeviceTwoFactorNotice account with null isTwoFactorEnabled returns true`() = runTest { - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDevicePermanentDismiss) - } returns true - every { - featureFlagManager.getFeatureFlag(FlagKey.NewDeviceTemporaryDismiss) - } returns true every { policyManager.getActivePolicies(type = PolicyTypeJson.REQUIRE_SSO) } returns listOf() diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt index 587ecd95de..1b6c096ae2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FlagKeyTest.kt @@ -66,4 +66,14 @@ class FlagKeyTest { fun `NewDeviceTemporaryDismiss is remotely configured value should be true`() { assertTrue(FlagKey.NewDeviceTemporaryDismiss.isRemotelyConfigured) } + + @Test + fun `IgnoreEnvironmentCheck default value should be false`() { + assertFalse(FlagKey.IgnoreEnvironmentCheck.defaultValue) + } + + @Test + fun `IgnoreEnvironmentCheck is remotely configured value should be false`() { + assertFalse(FlagKey.IgnoreEnvironmentCheck.isRemotelyConfigured) + } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt index 9d8dd79b81..691d453932 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuViewModelTest.kt @@ -119,6 +119,7 @@ private val DEFAULT_MAP_VALUE: Map, Any> = mapOf( FlagKey.AppReviewPrompt to true, FlagKey.NewDeviceTemporaryDismiss to true, FlagKey.NewDevicePermanentDismiss to true, + FlagKey.IgnoreEnvironmentCheck to true, ) private val UPDATED_MAP_VALUE: Map, Any> = mapOf( @@ -134,6 +135,7 @@ private val UPDATED_MAP_VALUE: Map, Any> = mapOf( FlagKey.AppReviewPrompt to false, FlagKey.NewDeviceTemporaryDismiss to false, FlagKey.NewDevicePermanentDismiss to false, + FlagKey.IgnoreEnvironmentCheck to false, ) private val DEFAULT_STATE = DebugMenuState(