mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 00:06:22 -06:00
[PM-28468] Added service methods to migration to MyItems validation (#6248)
This commit is contained in:
parent
cd27fe339d
commit
4a874668f2
@ -25,4 +25,10 @@ interface PolicyManager {
|
||||
userId: String,
|
||||
type: PolicyTypeJson,
|
||||
): List<SyncResponseJson.Policy>
|
||||
|
||||
/**
|
||||
* Get the organization id of the personal ownership policy.
|
||||
* If multiple organizations enforce the policy, return the first to set it.
|
||||
*/
|
||||
fun getPersonalOwnershipPolicyOrganizationId(): String?
|
||||
}
|
||||
|
||||
@ -66,6 +66,13 @@ class PolicyManagerImpl(
|
||||
)
|
||||
.orEmpty()
|
||||
|
||||
override fun getPersonalOwnershipPolicyOrganizationId(): String? =
|
||||
this
|
||||
.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.sortedBy { it.revisionDate }
|
||||
.firstOrNull()
|
||||
?.organizationId
|
||||
|
||||
/**
|
||||
* A helper method to filter policies.
|
||||
*/
|
||||
|
||||
@ -166,4 +166,11 @@ interface VaultRepository :
|
||||
* `null` if the item cannot be found.
|
||||
*/
|
||||
fun getVaultListItemStateFlow(itemId: String): StateFlow<DataState<CipherListView?>>
|
||||
|
||||
/**
|
||||
* Checks if there are any personal vault items (items without an organization ID) in the vault.
|
||||
*
|
||||
* @return `true` if there are personal vault items, `false` otherwise.
|
||||
*/
|
||||
fun hasPersonalVaultItems(): Boolean
|
||||
}
|
||||
|
||||
@ -550,4 +550,9 @@ class VaultRepositoryImpl(
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
}
|
||||
|
||||
override fun hasPersonalVaultItems(): Boolean {
|
||||
val vaultData = vaultSyncManager.vaultDataStateFlow.value.data ?: return false
|
||||
return vaultData.decryptCipherListResult.successes.any { it.organizationId.isNullOrEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,9 +13,11 @@ import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class PolicyManagerTest {
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserStateJson?>(null)
|
||||
@ -277,6 +279,207 @@ class PolicyManagerTest {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId returns null when no active user`() {
|
||||
every { authDiskSource.userState } returns null
|
||||
|
||||
assertNull(policyManager.getPersonalOwnershipPolicyOrganizationId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId returns null when no policies exist`() {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
every { authDiskSource.userState } returns userState
|
||||
every { authDiskSource.getOrganizations(USER_ID) } returns emptyList()
|
||||
every { authDiskSource.getPolicies(USER_ID) } returns emptyList()
|
||||
|
||||
assertNull(policyManager.getPersonalOwnershipPolicyOrganizationId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId returns null when policy is disabled`() {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
every { authDiskSource.userState } returns userState
|
||||
every {
|
||||
authDiskSource.getOrganizations(USER_ID)
|
||||
} returns listOf(
|
||||
createMockOrganization(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
)
|
||||
every {
|
||||
authDiskSource.getPolicies(USER_ID)
|
||||
} returns listOf(
|
||||
createMockPolicy(
|
||||
organizationId = "mockId-1",
|
||||
isEnabled = false,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
),
|
||||
)
|
||||
|
||||
assertNull(policyManager.getPersonalOwnershipPolicyOrganizationId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId returns organization id for single policy`() {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val expectedOrganizationId = "mockId-1"
|
||||
every { authDiskSource.userState } returns userState
|
||||
every {
|
||||
authDiskSource.getOrganizations(USER_ID)
|
||||
} returns listOf(
|
||||
createMockOrganization(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
)
|
||||
every {
|
||||
authDiskSource.getPolicies(USER_ID)
|
||||
} returns listOf(
|
||||
createMockPolicy(
|
||||
organizationId = expectedOrganizationId,
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedOrganizationId,
|
||||
policyManager.getPersonalOwnershipPolicyOrganizationId(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId returns earliest policy when multiple orgs have policy`() {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val earliestRevisionDate = ZonedDateTime.parse("2024-01-01T00:00:00Z")
|
||||
val middleRevisionDate = ZonedDateTime.parse("2024-06-01T00:00:00Z")
|
||||
val latestRevisionDate = ZonedDateTime.parse("2024-12-01T00:00:00Z")
|
||||
|
||||
val expectedOrganizationId = "mockId-1"
|
||||
|
||||
every { authDiskSource.userState } returns userState
|
||||
every {
|
||||
authDiskSource.getOrganizations(USER_ID)
|
||||
} returns listOf(
|
||||
createMockOrganization(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
createMockOrganization(
|
||||
number = 2,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
createMockOrganization(
|
||||
number = 3,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
)
|
||||
every {
|
||||
authDiskSource.getPolicies(USER_ID)
|
||||
} returns listOf(
|
||||
createMockPolicy(
|
||||
number = 3,
|
||||
organizationId = "mockId-3",
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
revisionDate = latestRevisionDate,
|
||||
),
|
||||
createMockPolicy(
|
||||
number = 1,
|
||||
organizationId = expectedOrganizationId,
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
revisionDate = earliestRevisionDate,
|
||||
),
|
||||
createMockPolicy(
|
||||
number = 2,
|
||||
organizationId = "mockId-2",
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
revisionDate = middleRevisionDate,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedOrganizationId,
|
||||
policyManager.getPersonalOwnershipPolicyOrganizationId(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getPersonalOwnershipPolicyOrganizationId filters out policies from organizations not using policies`() {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val earlierRevisionDate = ZonedDateTime.parse("2024-01-01T00:00:00Z")
|
||||
val laterRevisionDate = ZonedDateTime.parse("2024-06-01T00:00:00Z")
|
||||
val expectedOrganizationId = "mockId-2"
|
||||
|
||||
every { authDiskSource.userState } returns userState
|
||||
every {
|
||||
authDiskSource.getOrganizations(USER_ID)
|
||||
} returns listOf(
|
||||
createMockOrganization(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = false, // This org does NOT use policies
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
createMockOrganization(
|
||||
number = 2,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true, // This org uses policies
|
||||
type = OrganizationType.USER,
|
||||
),
|
||||
)
|
||||
every {
|
||||
authDiskSource.getPolicies(USER_ID)
|
||||
} returns listOf(
|
||||
createMockPolicy(
|
||||
number = 1,
|
||||
organizationId = "mockId-1",
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
revisionDate = earlierRevisionDate, // Earlier but org doesn't enforce
|
||||
),
|
||||
createMockPolicy(
|
||||
number = 2,
|
||||
organizationId = expectedOrganizationId,
|
||||
isEnabled = true,
|
||||
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
|
||||
revisionDate = laterRevisionDate,
|
||||
),
|
||||
)
|
||||
|
||||
// Should return mockId-2 because mockId-1's organization doesn't enforce policies
|
||||
assertEquals(
|
||||
expectedOrganizationId,
|
||||
policyManager.getPersonalOwnershipPolicyOrganizationId(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val USER_ID = "userId"
|
||||
|
||||
@ -1390,6 +1390,100 @@ class VaultRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasPersonalVaultItems returns false when vault data is loading`() {
|
||||
mutableVaultDataStateFlow.value = DataState.Loading
|
||||
|
||||
val result = vaultRepository.hasPersonalVaultItems()
|
||||
|
||||
assertEquals(false, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasPersonalVaultItems returns false when all items belong to organizations`() {
|
||||
mutableVaultDataStateFlow.value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
decryptCipherListResult = DecryptCipherListResult(
|
||||
successes = listOf(
|
||||
createMockCipherListView(number = 1, organizationId = "org-1"),
|
||||
createMockCipherListView(number = 2, organizationId = "org-2"),
|
||||
),
|
||||
failures = emptyList(),
|
||||
),
|
||||
collectionViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
sendViewList = emptyList(),
|
||||
),
|
||||
)
|
||||
|
||||
val result = vaultRepository.hasPersonalVaultItems()
|
||||
|
||||
assertEquals(false, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasPersonalVaultItems returns true when there are items without organization ID`() {
|
||||
mutableVaultDataStateFlow.value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
decryptCipherListResult = DecryptCipherListResult(
|
||||
successes = listOf(
|
||||
createMockCipherListView(number = 1, organizationId = null),
|
||||
createMockCipherListView(number = 2, organizationId = "org-2"),
|
||||
),
|
||||
failures = emptyList(),
|
||||
),
|
||||
collectionViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
sendViewList = emptyList(),
|
||||
),
|
||||
)
|
||||
|
||||
val result = vaultRepository.hasPersonalVaultItems()
|
||||
|
||||
assertEquals(true, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasPersonalVaultItems returns true when there are items with empty organization ID`() {
|
||||
mutableVaultDataStateFlow.value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
decryptCipherListResult = DecryptCipherListResult(
|
||||
successes = listOf(
|
||||
createMockCipherListView(number = 1, organizationId = ""),
|
||||
createMockCipherListView(number = 2, organizationId = "org-2"),
|
||||
),
|
||||
failures = emptyList(),
|
||||
),
|
||||
collectionViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
sendViewList = emptyList(),
|
||||
),
|
||||
)
|
||||
|
||||
val result = vaultRepository.hasPersonalVaultItems()
|
||||
|
||||
assertEquals(true, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasPersonalVaultItems returns false when successes list is empty`() {
|
||||
mutableVaultDataStateFlow.value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
decryptCipherListResult = DecryptCipherListResult(
|
||||
successes = emptyList(),
|
||||
failures = emptyList(),
|
||||
),
|
||||
collectionViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
sendViewList = emptyList(),
|
||||
),
|
||||
)
|
||||
|
||||
val result = vaultRepository.hasPersonalVaultItems()
|
||||
|
||||
assertEquals(false, result)
|
||||
}
|
||||
|
||||
//region Helper functions
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user