PM-11299: Update the userState to properly parse the hasManageResetPasswordPermission flag (#3820)

This commit is contained in:
David Perez 2024-08-23 11:11:20 -05:00 committed by GitHub
parent 162da64567
commit b7330392cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 74 additions and 11 deletions

View file

@ -7,12 +7,15 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
* *
* @property id The ID of the organization. * @property id The ID of the organization.
* @property name The name of the organization (if applicable). * @property name The name of the organization (if applicable).
* @property shouldManageResetPassword Indicates that this user has the permission to manage their
* own password.
* @property shouldUseKeyConnector Indicates that the organization uses a key connector. * @property shouldUseKeyConnector Indicates that the organization uses a key connector.
* @property role The user's role in the organization. * @property role The user's role in the organization.
*/ */
data class Organization( data class Organization(
val id: String, val id: String,
val name: String?, val name: String?,
val shouldManageResetPassword: Boolean,
val shouldUseKeyConnector: Boolean, val shouldUseKeyConnector: Boolean,
val role: OrganizationType, val role: OrganizationType,
) )

View file

@ -16,6 +16,7 @@ fun SyncResponseJson.Profile.Organization.toOrganization(): Organization =
name = this.name, name = this.name,
shouldUseKeyConnector = this.shouldUseKeyConnector, shouldUseKeyConnector = this.shouldUseKeyConnector,
role = this.type, role = this.type,
shouldManageResetPassword = this.permissions.shouldManageResetPassword,
) )
/** /**

View file

@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrlsOrDefault import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrlsOrDefault
import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.util.statusFor import com.x8bit.bitwarden.data.vault.repository.util.statusFor
@ -101,7 +102,7 @@ fun UserStateJson.toUserStateJsonWithPassword(): UserStateJson {
/** /**
* Converts the given [UserStateJson] to a [UserState] using the given [vaultState]. * Converts the given [UserStateJson] to a [UserState] using the given [vaultState].
*/ */
@Suppress("LongParameterList") @Suppress("LongParameterList", "LongMethod")
fun UserStateJson.toUserState( fun UserStateJson.toUserState(
vaultState: List<VaultUnlockData>, vaultState: List<VaultUnlockData>,
userAccountTokens: List<UserAccountTokens>, userAccountTokens: List<UserAccountTokens>,
@ -125,8 +126,17 @@ fun UserStateJson.toUserState(
val decryptionOptions = profile.userDecryptionOptions val decryptionOptions = profile.userDecryptionOptions
val trustedDeviceOptions = decryptionOptions?.trustedDeviceUserDecryptionOptions val trustedDeviceOptions = decryptionOptions?.trustedDeviceUserDecryptionOptions
val keyConnectorOptions = decryptionOptions?.keyConnectorUserDecryptionOptions val keyConnectorOptions = decryptionOptions?.keyConnectorUserDecryptionOptions
val organizations = userOrganizationsList
.find { it.userId == userId }
?.organizations
.orEmpty()
val hasManageResetPasswordPermission = organizations.any {
it.role == OrganizationType.OWNER ||
it.role == OrganizationType.ADMIN ||
it.shouldManageResetPassword
}
val needsMasterPassword = decryptionOptions?.hasMasterPassword == false && val needsMasterPassword = decryptionOptions?.hasMasterPassword == false &&
trustedDeviceOptions?.hasManageResetPasswordPermission != false && hasManageResetPasswordPermission &&
keyConnectorOptions == null keyConnectorOptions == null
val trustedDevice = trustedDeviceOptions?.let { val trustedDevice = trustedDeviceOptions?.let {
UserState.TrustedDevice( UserState.TrustedDevice(
@ -152,10 +162,7 @@ fun UserStateJson.toUserState(
?.isLoggedIn == true, ?.isLoggedIn == true,
isVaultUnlocked = vaultUnlocked, isVaultUnlocked = vaultUnlocked,
needsPasswordReset = needsPasswordReset, needsPasswordReset = needsPasswordReset,
organizations = userOrganizationsList organizations = organizations,
.find { it.userId == userId }
?.organizations
.orEmpty(),
isBiometricsEnabled = isBiometricsEnabledProvider(userId), isBiometricsEnabled = isBiometricsEnabledProvider(userId),
vaultUnlockType = vaultUnlockTypeProvider(userId), vaultUnlockType = vaultUnlockTypeProvider(userId),
needsMasterPassword = needsMasterPassword, needsMasterPassword = needsMasterPassword,

View file

@ -4277,6 +4277,9 @@ class AuthRepositoryTest {
mockk<SyncResponseJson.Profile.Organization> { mockk<SyncResponseJson.Profile.Organization> {
every { id } returns "orgId" every { id } returns "orgId"
every { name } returns "orgName" every { name } returns "orgName"
every { permissions } returns mockk {
every { shouldManageResetPassword } returns false
}
every { shouldUseKeyConnector } returns true every { shouldUseKeyConnector } returns true
every { type } returns OrganizationType.USER every { type } returns OrganizationType.USER
every { keyConnectorUrl } returns null every { keyConnectorUrl } returns null
@ -4299,6 +4302,9 @@ class AuthRepositoryTest {
mockk<SyncResponseJson.Profile.Organization> { mockk<SyncResponseJson.Profile.Organization> {
every { id } returns "orgId" every { id } returns "orgId"
every { name } returns "orgName" every { name } returns "orgName"
every { permissions } returns mockk {
every { shouldManageResetPassword } returns false
}
every { shouldUseKeyConnector } returns true every { shouldUseKeyConnector } returns true
every { type } returns OrganizationType.USER every { type } returns OrganizationType.USER
every { keyConnectorUrl } returns url every { keyConnectorUrl } returns url
@ -4332,6 +4338,9 @@ class AuthRepositoryTest {
mockk<SyncResponseJson.Profile.Organization> { mockk<SyncResponseJson.Profile.Organization> {
every { id } returns "orgId" every { id } returns "orgId"
every { name } returns "orgName" every { name } returns "orgName"
every { permissions } returns mockk {
every { shouldManageResetPassword } returns false
}
every { shouldUseKeyConnector } returns true every { shouldUseKeyConnector } returns true
every { type } returns OrganizationType.USER every { type } returns OrganizationType.USER
every { keyConnectorUrl } returns url every { keyConnectorUrl } returns url

View file

@ -189,6 +189,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-1", id = "mockId-1",
name = "mockName-1", name = "mockName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -200,6 +201,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-2", id = "mockId-2",
name = "mockName-2", name = "mockName-2",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -211,6 +213,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-3", id = "mockId-3",
name = "mockName-3", name = "mockName-3",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -357,6 +360,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-1", id = "mockId-1",
name = "mockName-1", name = "mockName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -387,6 +391,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-1", id = "mockId-1",
name = "mockName-1", name = "mockName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -398,6 +403,7 @@ class AuthDiskSourceExtensionsTest {
Organization( Organization(
id = "mockId-2", id = "mockId-2",
name = "mockName-2", name = "mockName-2",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -20,6 +20,7 @@ class SyncResponseJsonExtensionsTest {
Organization( Organization(
id = "mockId-1", id = "mockId-1",
name = "mockName-1", name = "mockName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -34,19 +35,22 @@ class SyncResponseJsonExtensionsTest {
Organization( Organization(
id = "mockId-1", id = "mockId-1",
name = "mockName-1", name = "mockName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = true, shouldUseKeyConnector = true,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "mockId-2", id = "mockId-2",
name = "mockName-2", name = "mockName-2",
shouldManageResetPassword = true,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.USER, role = OrganizationType.USER,
), ),
), ),
listOf( listOf(
createMockOrganization(number = 1).copy(shouldUseKeyConnector = true), createMockOrganization(number = 1).copy(shouldUseKeyConnector = true),
createMockOrganization(number = 2).copy(type = OrganizationType.USER), createMockOrganization(number = 2, shouldManageResetPassword = true)
.copy(type = OrganizationType.USER),
) )
.toOrganizations(), .toOrganizations(),
) )

View file

@ -342,6 +342,7 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -403,6 +404,7 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -444,6 +446,7 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -501,6 +504,7 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -543,13 +547,14 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
), ),
isBiometricsEnabled = false, isBiometricsEnabled = false,
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD, vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
needsMasterPassword = false, needsMasterPassword = true,
trustedDevice = UserState.TrustedDevice( trustedDevice = UserState.TrustedDevice(
isDeviceTrusted = true, isDeviceTrusted = true,
hasAdminApproval = false, hasAdminApproval = false,
@ -608,6 +613,7 @@ class UserStateJsonExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -34,6 +34,7 @@ fun createMockOrganization(
number: Int, number: Int,
isEnabled: Boolean = false, isEnabled: Boolean = false,
shouldUsePolicies: Boolean = false, shouldUsePolicies: Boolean = false,
shouldManageResetPassword: Boolean = false,
): SyncResponseJson.Profile.Organization = ): SyncResponseJson.Profile.Organization =
SyncResponseJson.Profile.Organization( SyncResponseJson.Profile.Organization(
shouldUsePolicies = shouldUsePolicies, shouldUsePolicies = shouldUsePolicies,
@ -45,7 +46,7 @@ fun createMockOrganization(
providerType = 1, providerType = 1,
maxCollections = 1, maxCollections = 1,
isSelfHost = false, isSelfHost = false,
permissions = createMockPermissions(), permissions = createMockPermissions(shouldManageResetPassword = shouldManageResetPassword),
providerId = "mockProviderId-$number", providerId = "mockProviderId-$number",
id = "mockId-$number", id = "mockId-$number",
shouldUseGroups = false, shouldUseGroups = false,
@ -78,10 +79,12 @@ fun createMockOrganizationKeys(number: Int): Map<String, String> =
/** /**
* Create a mock [SyncResponseJson.Profile.Permissions]. * Create a mock [SyncResponseJson.Profile.Permissions].
*/ */
fun createMockPermissions(): SyncResponseJson.Profile.Permissions = fun createMockPermissions(
shouldManageResetPassword: Boolean = false,
): SyncResponseJson.Profile.Permissions =
SyncResponseJson.Profile.Permissions( SyncResponseJson.Profile.Permissions(
shouldManageGroups = false, shouldManageGroups = false,
shouldManageResetPassword = false, shouldManageResetPassword = shouldManageResetPassword,
shouldAccessReports = false, shouldAccessReports = false,
shouldManagePolicies = false, shouldManagePolicies = false,
shouldDeleteAnyCollection = false, shouldDeleteAnyCollection = false,

View file

@ -156,6 +156,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
Organization( Organization(
id = "orgId", id = "orgId",
name = ORGANIZATION_NAME, name = ORGANIZATION_NAME,
shouldManageResetPassword = false,
shouldUseKeyConnector = true, shouldUseKeyConnector = true,
role = OrganizationType.USER, role = OrganizationType.USER,
), ),

View file

@ -341,6 +341,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
Organization( Organization(
id = "orgId", id = "orgId",
name = "orgName", name = "orgName",
shouldManageResetPassword = false,
shouldUseKeyConnector = true, shouldUseKeyConnector = true,
role = OrganizationType.USER, role = OrganizationType.USER,
), ),

View file

@ -3879,6 +3879,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -435,6 +435,7 @@ class CipherViewExtensionsTest {
Organization( Organization(
id = "mockOrganizationId-1", id = "mockOrganizationId-1",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -491,18 +491,21 @@ private val DEFAULT_USER_STATE = UserState(
Organization( Organization(
id = "mockOrganizationId-1", id = "mockOrganizationId-1",
name = "mockOrganizationName-1", name = "mockOrganizationName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "mockOrganizationId-2", id = "mockOrganizationId-2",
name = "mockOrganizationName-2", name = "mockOrganizationName-2",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "mockOrganizationId-3", id = "mockOrganizationId-3",
name = "mockOrganizationName-3", name = "mockOrganizationName-3",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -103,18 +103,21 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState =
Organization( Organization(
id = "mockOrganizationId-1", id = "mockOrganizationId-1",
name = "mockOrganizationName-1", name = "mockOrganizationName-1",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "mockOrganizationId-2", id = "mockOrganizationId-2",
name = "mockOrganizationName-2", name = "mockOrganizationName-2",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "mockOrganizationId-3", id = "mockOrganizationId-3",
name = "mockOrganizationName-3", name = "mockOrganizationName-3",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -188,6 +188,7 @@ class VaultViewModelTest : BaseViewModelTest() {
Organization( Organization(
id = "organiationId", id = "organiationId",
name = "Test Organization", name = "Test Organization",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -271,6 +272,7 @@ class VaultViewModelTest : BaseViewModelTest() {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "Test Organization", name = "Test Organization",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -478,6 +480,7 @@ class VaultViewModelTest : BaseViewModelTest() {
Organization( Organization(
id = "testOrganizationId", id = "testOrganizationId",
name = "Test Organization", name = "Test Organization",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),

View file

@ -77,6 +77,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -101,6 +102,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -129,6 +131,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -157,6 +160,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -200,6 +204,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -241,6 +246,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -286,6 +292,7 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId", id = "organizationId",
name = "organizationName", name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -360,11 +367,13 @@ class UserStateExtensionsTest {
id = "organizationId-B", id = "organizationId-B",
name = "Organization B", name = "Organization B",
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
shouldManageResetPassword = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "organizationId-A", id = "organizationId-A",
name = "Organization A", name = "Organization A",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
@ -413,12 +422,14 @@ class UserStateExtensionsTest {
Organization( Organization(
id = "organizationId-B", id = "organizationId-B",
name = "Organization B", name = "Organization B",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),
Organization( Organization(
id = "organizationId-A", id = "organizationId-A",
name = "Organization A", name = "Organization A",
shouldManageResetPassword = false,
shouldUseKeyConnector = false, shouldUseKeyConnector = false,
role = OrganizationType.ADMIN, role = OrganizationType.ADMIN,
), ),