mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 08:55:48 +03:00
PM-12683 SSO user needed password set bug (#4018)
This commit is contained in:
parent
e6eb626d85
commit
fd6b276cc8
2 changed files with 391 additions and 4 deletions
|
@ -137,9 +137,6 @@ fun UserStateJson.toUserState(
|
|||
it.role == OrganizationType.ADMIN ||
|
||||
it.shouldManageResetPassword
|
||||
}
|
||||
val needsMasterPassword = decryptionOptions?.hasMasterPassword == false &&
|
||||
hasManageResetPasswordPermission &&
|
||||
keyConnectorOptions == null
|
||||
val trustedDevice = trustedDeviceOptions?.let {
|
||||
UserState.TrustedDevice(
|
||||
isDeviceTrusted = isDeviceTrustedProvider(userId),
|
||||
|
@ -148,7 +145,14 @@ fun UserStateJson.toUserState(
|
|||
hasResetPasswordPermission = it.hasManageResetPasswordPermission,
|
||||
)
|
||||
}
|
||||
|
||||
// If a user does not have a Master Password we want to check if they have another
|
||||
// method for unlocking the vault. In the case of a TDE user we check if they
|
||||
// have the reset password permission via their organization(S). If the user does
|
||||
// not belong to a TDE or we check to see if they user key connector.
|
||||
val tdeUserNeedsMasterPassword =
|
||||
hasManageResetPasswordPermission.takeIf { trustedDevice != null }
|
||||
val needsMasterPassword = decryptionOptions?.hasMasterPassword == false &&
|
||||
(tdeUserNeedsMasterPassword ?: (keyConnectorOptions == null))
|
||||
UserState.Account(
|
||||
userId = userId,
|
||||
name = profile.name,
|
||||
|
|
|
@ -865,4 +865,387 @@ class UserStateJsonExtensionsTest {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toUserState should set true for needsMasterPassword for TDE user with permission through organization`() {
|
||||
assertEquals(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "activeName",
|
||||
email = "activeEmail",
|
||||
avatarColorHex = "#ffecbc49",
|
||||
environment = Environment.Eu,
|
||||
isPremium = true,
|
||||
isLoggedIn = false,
|
||||
isVaultUnlocked = false,
|
||||
needsPasswordReset = false,
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "organizationId",
|
||||
name = "organizationName",
|
||||
// Key part of the result #1, this is true or the role is owner or
|
||||
// admin
|
||||
shouldManageResetPassword = true,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.USER,
|
||||
),
|
||||
),
|
||||
isBiometricsEnabled = false,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
needsMasterPassword = true,
|
||||
// Key part of the result #2, TDE options should exist
|
||||
trustedDevice = UserState.TrustedDevice(
|
||||
isDeviceTrusted = true,
|
||||
hasAdminApproval = false,
|
||||
hasLoginApprovingDevice = true,
|
||||
hasResetPasswordPermission = false,
|
||||
),
|
||||
// Key part of the result #3, options should have false for
|
||||
// hasMasterPassword
|
||||
hasMasterPassword = false,
|
||||
isUsingKeyConnector = true,
|
||||
onboardingStatus = OnboardingStatus.COMPLETE,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
),
|
||||
UserStateJson(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = mapOf(
|
||||
"activeUserId" to AccountJson(
|
||||
profile = mockk {
|
||||
every { userId } returns "activeUserId"
|
||||
every { name } returns "activeName"
|
||||
every { email } returns "activeEmail"
|
||||
every { avatarColorHex } returns null
|
||||
every { hasPremium } returns true
|
||||
every { forcePasswordResetReason } returns null
|
||||
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
|
||||
hasMasterPassword = false,
|
||||
trustedDeviceUserDecryptionOptions = TrustedDeviceUserDecryptionOptionsJson(
|
||||
encryptedPrivateKey = null,
|
||||
encryptedUserKey = null,
|
||||
hasAdminApproval = false,
|
||||
hasLoginApprovingDevice = true,
|
||||
hasManageResetPasswordPermission = false,
|
||||
),
|
||||
keyConnectorUserDecryptionOptions = null,
|
||||
)
|
||||
},
|
||||
tokens = null,
|
||||
settings = AccountJson.Settings(
|
||||
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toUserState(
|
||||
vaultState = emptyList(),
|
||||
userAccountTokens = listOf(
|
||||
UserAccountTokens(
|
||||
userId = "activeUserId",
|
||||
accessToken = null,
|
||||
refreshToken = null,
|
||||
),
|
||||
),
|
||||
userOrganizationsList = listOf(
|
||||
UserOrganizations(
|
||||
userId = "activeUserId",
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "organizationId",
|
||||
name = "organizationName",
|
||||
shouldManageResetPassword = true,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.USER,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
userIsUsingKeyConnectorList = listOf(
|
||||
UserKeyConnectorState(
|
||||
userId = "activeUserId",
|
||||
isUsingKeyConnector = true,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
isBiometricsEnabledProvider = { false },
|
||||
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
|
||||
isDeviceTrustedProvider = { true },
|
||||
onboardingStatus = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toUserState should set true for needsMasterPassword for SSO user with no key connector`() {
|
||||
assertEquals(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "activeName",
|
||||
email = "activeEmail",
|
||||
avatarColorHex = "#ffecbc49",
|
||||
environment = Environment.Eu,
|
||||
isPremium = false,
|
||||
isLoggedIn = false,
|
||||
isVaultUnlocked = false,
|
||||
needsPasswordReset = false,
|
||||
organizations = emptyList(),
|
||||
isBiometricsEnabled = false,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = null,
|
||||
hasMasterPassword = false,
|
||||
isUsingKeyConnector = false,
|
||||
onboardingStatus = OnboardingStatus.COMPLETE,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
),
|
||||
UserStateJson(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = mapOf(
|
||||
"activeUserId" to AccountJson(
|
||||
profile = mockk {
|
||||
every { userId } returns "activeUserId"
|
||||
every { name } returns "activeName"
|
||||
every { email } returns "activeEmail"
|
||||
every { avatarColorHex } returns null
|
||||
every { hasPremium } returns false
|
||||
every { forcePasswordResetReason } returns null
|
||||
// The decryption options are what are determining the result
|
||||
@Suppress("MaxLineLength")
|
||||
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
|
||||
hasMasterPassword = false,
|
||||
trustedDeviceUserDecryptionOptions = null,
|
||||
keyConnectorUserDecryptionOptions = KeyConnectorUserDecryptionOptionsJson(
|
||||
keyConnectorUrl = "keyConnectorUrl",
|
||||
),
|
||||
)
|
||||
},
|
||||
tokens = null,
|
||||
settings = AccountJson.Settings(
|
||||
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toUserState(
|
||||
vaultState = emptyList(),
|
||||
userAccountTokens = listOf(
|
||||
UserAccountTokens(
|
||||
userId = "activeUserId",
|
||||
accessToken = null,
|
||||
refreshToken = null,
|
||||
),
|
||||
),
|
||||
userOrganizationsList = emptyList(),
|
||||
userIsUsingKeyConnectorList = emptyList(),
|
||||
hasPendingAccountAddition = true,
|
||||
isBiometricsEnabledProvider = { false },
|
||||
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
|
||||
isDeviceTrustedProvider = { true },
|
||||
onboardingStatus = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toUserState should set false for needsMasterPassword for SSO user with key connector`() {
|
||||
assertEquals(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "activeName",
|
||||
email = "activeEmail",
|
||||
avatarColorHex = "#ffecbc49",
|
||||
environment = Environment.Eu,
|
||||
isPremium = false,
|
||||
isLoggedIn = false,
|
||||
isVaultUnlocked = false,
|
||||
needsPasswordReset = false,
|
||||
organizations = emptyList(),
|
||||
isBiometricsEnabled = false,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
needsMasterPassword = true,
|
||||
trustedDevice = null,
|
||||
hasMasterPassword = false,
|
||||
isUsingKeyConnector = true,
|
||||
onboardingStatus = OnboardingStatus.COMPLETE,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
),
|
||||
UserStateJson(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = mapOf(
|
||||
"activeUserId" to AccountJson(
|
||||
profile = mockk {
|
||||
every { userId } returns "activeUserId"
|
||||
every { name } returns "activeName"
|
||||
every { email } returns "activeEmail"
|
||||
every { avatarColorHex } returns null
|
||||
every { hasPremium } returns false
|
||||
every { forcePasswordResetReason } returns null
|
||||
// The decryption options are what are determining the result
|
||||
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
|
||||
hasMasterPassword = false,
|
||||
trustedDeviceUserDecryptionOptions = null,
|
||||
keyConnectorUserDecryptionOptions = null,
|
||||
)
|
||||
},
|
||||
tokens = null,
|
||||
settings = AccountJson.Settings(
|
||||
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toUserState(
|
||||
vaultState = emptyList(),
|
||||
userAccountTokens = listOf(
|
||||
UserAccountTokens(
|
||||
userId = "activeUserId",
|
||||
accessToken = null,
|
||||
refreshToken = null,
|
||||
),
|
||||
),
|
||||
userOrganizationsList = emptyList(),
|
||||
userIsUsingKeyConnectorList = listOf(
|
||||
UserKeyConnectorState(
|
||||
userId = "activeUserId",
|
||||
isUsingKeyConnector = true,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
isBiometricsEnabledProvider = { false },
|
||||
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
|
||||
isDeviceTrustedProvider = { true },
|
||||
onboardingStatus = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `toUserState should set false for needsMasterPassword for SSO user with TDE but no permission via organization`() {
|
||||
assertEquals(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "activeName",
|
||||
email = "activeEmail",
|
||||
avatarColorHex = "#ffecbc49",
|
||||
environment = Environment.Eu,
|
||||
isPremium = false,
|
||||
isLoggedIn = false,
|
||||
isVaultUnlocked = false,
|
||||
needsPasswordReset = false,
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "organizationId",
|
||||
name = "organizationName",
|
||||
// Key part of the result #1, this is true or the role is owner or
|
||||
// admin
|
||||
shouldManageResetPassword = false,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.USER,
|
||||
),
|
||||
),
|
||||
isBiometricsEnabled = false,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = UserState.TrustedDevice(
|
||||
isDeviceTrusted = true,
|
||||
hasAdminApproval = false,
|
||||
hasLoginApprovingDevice = true,
|
||||
hasResetPasswordPermission = false,
|
||||
),
|
||||
hasMasterPassword = false,
|
||||
isUsingKeyConnector = true,
|
||||
onboardingStatus = OnboardingStatus.COMPLETE,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
),
|
||||
UserStateJson(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = mapOf(
|
||||
"activeUserId" to AccountJson(
|
||||
profile = mockk {
|
||||
every { userId } returns "activeUserId"
|
||||
every { name } returns "activeName"
|
||||
every { email } returns "activeEmail"
|
||||
every { avatarColorHex } returns null
|
||||
every { hasPremium } returns false
|
||||
every { forcePasswordResetReason } returns null
|
||||
// The decryption options are what are determining the result
|
||||
@Suppress("MaxLineLength")
|
||||
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
|
||||
hasMasterPassword = false,
|
||||
trustedDeviceUserDecryptionOptions = TrustedDeviceUserDecryptionOptionsJson(
|
||||
encryptedPrivateKey = null,
|
||||
encryptedUserKey = null,
|
||||
hasAdminApproval = false,
|
||||
hasLoginApprovingDevice = true,
|
||||
hasManageResetPasswordPermission = false,
|
||||
),
|
||||
keyConnectorUserDecryptionOptions = null,
|
||||
)
|
||||
},
|
||||
tokens = null,
|
||||
settings = AccountJson.Settings(
|
||||
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toUserState(
|
||||
vaultState = emptyList(),
|
||||
userAccountTokens = listOf(
|
||||
UserAccountTokens(
|
||||
userId = "activeUserId",
|
||||
accessToken = null,
|
||||
refreshToken = null,
|
||||
),
|
||||
),
|
||||
userOrganizationsList = listOf(
|
||||
UserOrganizations(
|
||||
userId = "activeUserId",
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "organizationId",
|
||||
name = "organizationName",
|
||||
shouldManageResetPassword = false,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.USER,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
userIsUsingKeyConnectorList = listOf(
|
||||
UserKeyConnectorState(
|
||||
userId = "activeUserId",
|
||||
isUsingKeyConnector = true,
|
||||
),
|
||||
),
|
||||
hasPendingAccountAddition = true,
|
||||
isBiometricsEnabledProvider = { false },
|
||||
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
|
||||
isDeviceTrustedProvider = { true },
|
||||
onboardingStatus = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue