From a1c627609263b5201fa63c812cbe9671a9deb710 Mon Sep 17 00:00:00 2001 From: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com> Date: Thu, 21 Aug 2025 09:57:19 -0400 Subject: [PATCH] [PM-25057] Filter Card Autofill Ciphers by Policy (#5768) --- .../data/autofill/di/AutofillModule.kt | 2 ++ .../provider/AutofillCipherProviderImpl.kt | 11 +++++++-- .../processor/AutofillCipherProviderTest.kt | 23 ++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt index 103ebcbb7c..d8689d6cd4 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/di/AutofillModule.kt @@ -108,11 +108,13 @@ object AutofillModule { authRepository: AuthRepository, cipherMatchingManager: CipherMatchingManager, vaultRepository: VaultRepository, + policyManager: PolicyManager, ): AutofillCipherProvider = AutofillCipherProviderImpl( authRepository = authRepository, cipherMatchingManager = cipherMatchingManager, vaultRepository = vaultRepository, + policyManager = policyManager, ) @Singleton diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt index f07c1bd01f..ea232e7a35 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt @@ -1,11 +1,13 @@ package com.x8bit.bitwarden.data.autofill.provider +import com.bitwarden.network.model.PolicyTypeJson import com.bitwarden.vault.CipherListView import com.bitwarden.vault.CipherListViewType import com.bitwarden.vault.CipherRepromptType import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.autofill.model.AutofillCipher +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager import com.x8bit.bitwarden.data.platform.util.firstWithTimeoutOrNull import com.x8bit.bitwarden.data.platform.util.subtitle @@ -34,6 +36,7 @@ class AutofillCipherProviderImpl( private val authRepository: AuthRepository, private val cipherMatchingManager: CipherMatchingManager, private val vaultRepository: VaultRepository, + private val policyManager: PolicyManager, ) : AutofillCipherProvider { private val activeUserId: String? get() = authRepository.activeUserId @@ -53,7 +56,9 @@ class AutofillCipherProviderImpl( override suspend fun getCardAutofillCiphers(): List { val cipherListViews = getUnlockedCipherListViewsOrNull() ?: return emptyList() - + val organizationIdsWithCardTypeRestrictions = policyManager + .getActivePolicies(PolicyTypeJson.RESTRICT_ITEM_TYPES) + .map { it.organizationId } return cipherListViews .mapNotNull { cipherListView -> cipherListView @@ -64,7 +69,9 @@ class AutofillCipherProviderImpl( // Must not be deleted. it.deletedDate == null && // Must not require a reprompt. - it.reprompt == CipherRepromptType.NONE + it.reprompt == CipherRepromptType.NONE && + // Must not be restricted by organization. + it.organizationId !in organizationIdsWithCardTypeRestrictions } ?.let { nonNullCipherListView -> nonNullCipherListView.id?.let { cipherId -> diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt index a36a34261c..ad5fbf8983 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt @@ -1,6 +1,8 @@ package com.x8bit.bitwarden.data.autofill.processor import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.network.model.PolicyTypeJson +import com.bitwarden.network.model.createMockPolicy import com.bitwarden.vault.CardListView import com.bitwarden.vault.CardView import com.bitwarden.vault.CipherListView @@ -17,6 +19,7 @@ import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProviderImpl import com.x8bit.bitwarden.data.autofill.util.card import com.x8bit.bitwarden.data.autofill.util.login +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager import com.x8bit.bitwarden.data.platform.util.subtitle import com.x8bit.bitwarden.data.vault.manager.model.GetCipherResult @@ -54,6 +57,7 @@ class AutofillCipherProviderTest { every { id } returns CARD_CIPHER_ID every { name } returns CARD_NAME every { reprompt } returns CipherRepromptType.NONE + every { organizationId } returns null every { type } returns CipherListViewType.Card(v1 = cardListView) } private val cardView: CardView = mockk { @@ -140,6 +144,11 @@ class AutofillCipherProviderTest { mutableVaultStateFlow.value.statusFor(ACTIVE_USER_ID) == VaultUnlockData.Status.UNLOCKED } } + private val policyManager: PolicyManager = mockk { + every { + getActivePolicies(PolicyTypeJson.RESTRICT_ITEM_TYPES) + } returns emptyList() + } private lateinit var autofillCipherProvider: AutofillCipherProvider @@ -150,6 +159,7 @@ class AutofillCipherProviderTest { authRepository = authRepository, cipherMatchingManager = cipherMatchingManager, vaultRepository = vaultRepository, + policyManager = policyManager, ) } @@ -254,7 +264,7 @@ class AutofillCipherProviderTest { @Suppress("MaxLineLength") @Test - fun `getCardAutofillCiphers when unlocked should decrypt then return non-null, non-deleted, and non-reprompt card ciphers`() = + fun `getCardAutofillCiphers when unlocked should decrypt then return non-null, non-deleted, non-reprompt, and non-restricted card ciphers`() = runTest { val deletedCardCipherView: CipherListView = mockk { every { deletedDate } returns mockk() @@ -265,17 +275,27 @@ class AutofillCipherProviderTest { every { reprompt } returns CipherRepromptType.PASSWORD every { type } returns CipherListViewType.Card(cardListView) } + val restrictedCardCipherView: CipherListView = mockk { + every { deletedDate } returns null + every { type } returns CipherListViewType.Card(cardListView) + every { reprompt } returns CipherRepromptType.NONE + every { organizationId } returns ORGANIZATION_ID + } val decryptCipherListViewsResult = DecryptCipherListResult( successes = listOf( cardCipherListView, deletedCardCipherView, repromptCardCipherView, + restrictedCardCipherView, loginCipherListViewWithTotp, loginCipherListViewWithoutTotp, ), failures = emptyList(), ) + every { + policyManager.getActivePolicies(PolicyTypeJson.RESTRICT_ITEM_TYPES) + } returns listOf(createMockPolicy(number = 1, organizationId = ORGANIZATION_ID)) coEvery { vaultRepository.getCipher(CARD_CIPHER_ID) } returns GetCipherResult.Success( @@ -542,6 +562,7 @@ class AutofillCipherProviderTest { } private const val ACTIVE_USER_ID = "activeUserId" +private const val ORGANIZATION_ID = "organizationId" private const val CARD_CARDHOLDER_NAME = "John Doe" private const val CARD_CODE = "123" private const val CARD_EXP_MONTH = "January"