Skip to content

Commit 2aa371a

Browse files
authored
[PM-18050] Remove pin policy (#4718)
1 parent 00f30c9 commit 2aa371a

File tree

5 files changed

+107
-15
lines changed

5 files changed

+107
-15
lines changed

app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/PolicyTypeJson.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ enum class PolicyTypeJson {
8282
@SerialName("11")
8383
ACTIVATE_AUTOFILL,
8484

85+
/**
86+
* Hides the setting to "Unlock with Pin".
87+
*/
88+
@SerialName("14")
89+
REMOVE_UNLOCK_WITH_PIN,
90+
8591
/**
8692
* Represents an unknown policy type.
8793
*

app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreen.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -258,19 +258,21 @@ fun AccountSecurityScreen(
258258
.fillMaxWidth()
259259
.standardHorizontalMargin(),
260260
)
261-
Spacer(modifier = Modifier.height(height = 8.dp))
262-
BitwardenUnlockWithPinSwitch(
263-
isUnlockWithPasswordEnabled = state.isUnlockWithPasswordEnabled,
264-
isUnlockWithPinEnabled = state.isUnlockWithPinEnabled,
265-
onUnlockWithPinToggleAction = remember(viewModel) {
266-
{ viewModel.trySendAction(AccountSecurityAction.UnlockWithPinToggle(it)) }
267-
},
268-
cardStyle = CardStyle.Full,
269-
modifier = Modifier
270-
.testTag("UnlockWithPinSwitch")
271-
.fillMaxWidth()
272-
.standardHorizontalMargin(),
273-
)
261+
if (!state.removeUnlockWithPinPolicyEnabled || state.isUnlockWithPinEnabled) {
262+
Spacer(modifier = Modifier.height(height = 8.dp))
263+
BitwardenUnlockWithPinSwitch(
264+
isUnlockWithPasswordEnabled = state.isUnlockWithPasswordEnabled,
265+
isUnlockWithPinEnabled = state.isUnlockWithPinEnabled,
266+
onUnlockWithPinToggleAction = remember(viewModel) {
267+
{ viewModel.trySendAction(AccountSecurityAction.UnlockWithPinToggle(it)) }
268+
},
269+
cardStyle = CardStyle.Full,
270+
modifier = Modifier
271+
.testTag("UnlockWithPinSwitch")
272+
.fillMaxWidth()
273+
.standardHorizontalMargin(),
274+
)
275+
}
274276
Spacer(Modifier.height(16.dp))
275277
if (state.shouldShowEnableAuthenticatorSync) {
276278
SyncWithAuthenticatorRow(

app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class AccountSecurityViewModel @Inject constructor(
8282
vaultTimeoutPolicyMinutes = null,
8383
vaultTimeoutPolicyAction = null,
8484
shouldShowUnlockActionCard = false,
85+
removeUnlockWithPinPolicyEnabled = false,
8586
)
8687
},
8788
) {
@@ -111,6 +112,16 @@ class AccountSecurityViewModel @Inject constructor(
111112
.onEach(::sendAction)
112113
.launchIn(viewModelScope)
113114

115+
policyManager
116+
.getActivePoliciesFlow(type = PolicyTypeJson.REMOVE_UNLOCK_WITH_PIN)
117+
.map { policies ->
118+
AccountSecurityAction.Internal.RemovePinPolicyUpdateReceive(
119+
removeUnlockWithPinPolicyEnabled = policies.isNotEmpty(),
120+
)
121+
}
122+
.onEach(::sendAction)
123+
.launchIn(viewModelScope)
124+
114125
featureFlagManager
115126
.getFeatureFlagFlow(FlagKey.AuthenticatorSync)
116127
.onEach {
@@ -390,6 +401,20 @@ class AccountSecurityViewModel @Inject constructor(
390401
is AccountSecurityAction.Internal.PinProtectedLockUpdate -> {
391402
handlePinProtectedLockUpdate(action)
392403
}
404+
405+
is AccountSecurityAction.Internal.RemovePinPolicyUpdateReceive -> {
406+
handleRemovePinPolicyUpdate(action)
407+
}
408+
}
409+
}
410+
411+
private fun handleRemovePinPolicyUpdate(
412+
action: AccountSecurityAction.Internal.RemovePinPolicyUpdateReceive,
413+
) {
414+
mutableStateFlow.update {
415+
it.copy(
416+
removeUnlockWithPinPolicyEnabled = action.removeUnlockWithPinPolicyEnabled,
417+
)
393418
}
394419
}
395420

@@ -524,6 +549,7 @@ data class AccountSecurityState(
524549
val vaultTimeoutPolicyMinutes: Int?,
525550
val vaultTimeoutPolicyAction: String?,
526551
val shouldShowUnlockActionCard: Boolean,
552+
val removeUnlockWithPinPolicyEnabled: Boolean,
527553
) : Parcelable {
528554
/**
529555
* Indicates that there is a mechanism for unlocking your vault in place.
@@ -787,6 +813,13 @@ sealed class AccountSecurityAction {
787813
val vaultTimeoutPolicies: List<PolicyInformation.VaultTimeout>?,
788814
) : Internal()
789815

816+
/**
817+
* A remove pin policy update has been received.
818+
*/
819+
data class RemovePinPolicyUpdateReceive(
820+
val removeUnlockWithPinPolicyEnabled: Boolean,
821+
) : Internal()
822+
790823
/**
791824
* The show unlock badge update has been received.
792825
*/

app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityScreenTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,28 @@ class AccountSecurityScreenTest : BaseComposeTest() {
241241
composeTestRule.onNodeWithText("Unlock with Biometrics").assertIsOn()
242242
}
243243

244+
@Test
245+
fun `unlock with pin toggle should be displayed according to state`() {
246+
val toggleText = "Unlock with PIN code"
247+
composeTestRule.onNodeWithText(toggleText).performScrollTo().assertIsDisplayed()
248+
249+
mutableStateFlow.update {
250+
DEFAULT_STATE.copy(
251+
removeUnlockWithPinPolicyEnabled = true,
252+
isUnlockWithPinEnabled = true,
253+
)
254+
}
255+
composeTestRule.onNodeWithText(toggleText).performScrollTo().assertIsDisplayed()
256+
257+
mutableStateFlow.update {
258+
DEFAULT_STATE.copy(
259+
removeUnlockWithPinPolicyEnabled = true,
260+
isUnlockWithPinEnabled = false,
261+
)
262+
}
263+
composeTestRule.onNodeWithText(toggleText).assertDoesNotExist()
264+
}
265+
244266
@Test
245267
fun `on unlock with pin toggle when enabled should send UnlockWithPinToggle Disabled`() {
246268
mutableStateFlow.update {
@@ -1541,4 +1563,5 @@ private val DEFAULT_STATE = AccountSecurityState(
15411563
vaultTimeoutPolicyMinutes = null,
15421564
vaultTimeoutPolicyAction = null,
15431565
shouldShowUnlockActionCard = false,
1566+
removeUnlockWithPinPolicyEnabled = false,
15441567
)

app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentReposito
2525
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
2626
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
2727
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
28-
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
28+
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson.Policy
2929
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
3030
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
3131
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
@@ -81,7 +81,8 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
8181
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
8282
every { storeShowUnlockSettingBadge(any()) } just runs
8383
}
84-
private val mutableActivePolicyFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Policy>>()
84+
private val mutableActivePolicyFlow = bufferedMutableSharedFlow<List<Policy>>()
85+
private val mutableRemovePinPolicyFlow = bufferedMutableSharedFlow<List<Policy>>()
8586
private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk {
8687
every { createCipherOrNull(DEFAULT_USER_STATE.activeUserId) } returns CIPHER
8788
every { getOrCreateCipher(DEFAULT_USER_STATE.activeUserId) } returns CIPHER
@@ -96,6 +97,9 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
9697
every {
9798
getActivePoliciesFlow(type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT)
9899
} returns mutableActivePolicyFlow
100+
every {
101+
getActivePoliciesFlow(type = PolicyTypeJson.REMOVE_UNLOCK_WITH_PIN)
102+
} returns mutableRemovePinPolicyFlow
99103
}
100104
private val featureFlagManager: FeatureFlagManager = mockk(relaxed = true) {
101105
every { getFeatureFlag(FlagKey.AuthenticatorSync) } returns false
@@ -164,6 +168,29 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
164168
}
165169
}
166170

171+
@Test
172+
fun `state updates when remove pin policies change`() = runTest {
173+
val viewModel = createViewModel()
174+
175+
mutableRemovePinPolicyFlow.emit(
176+
listOf(
177+
createMockPolicy(
178+
isEnabled = true,
179+
type = PolicyTypeJson.REMOVE_UNLOCK_WITH_PIN,
180+
),
181+
),
182+
)
183+
184+
viewModel.stateFlow.test {
185+
assertEquals(
186+
DEFAULT_STATE.copy(
187+
removeUnlockWithPinPolicyEnabled = true,
188+
),
189+
awaitItem(),
190+
)
191+
}
192+
}
193+
167194
@Suppress("MaxLineLength")
168195
@Test
169196
fun `on AuthenticatorSyncToggle should update SettingsRepository and isAuthenticatorSyncChecked`() =
@@ -907,4 +934,5 @@ private val DEFAULT_STATE: AccountSecurityState = AccountSecurityState(
907934
vaultTimeoutPolicyAction = null,
908935
shouldShowEnableAuthenticatorSync = false,
909936
shouldShowUnlockActionCard = false,
937+
removeUnlockWithPinPolicyEnabled = false,
910938
)

0 commit comments

Comments
 (0)