[PM-14589] Prevent SSH key item creation (#4251)

This commit is contained in:
Patrick Honkonen 2024-11-11 11:52:53 -05:00 committed by GitHub
parent a3ed2bc068
commit 2c40a7f105
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 133 deletions

View file

@ -16,12 +16,10 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult
import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
@ -103,7 +101,6 @@ class VaultAddEditViewModel @Inject constructor(
private val resourceManager: ResourceManager, private val resourceManager: ResourceManager,
private val clock: Clock, private val clock: Clock,
private val organizationEventManager: OrganizationEventManager, private val organizationEventManager: OrganizationEventManager,
private val featureFlagManager: FeatureFlagManager,
) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>( ) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>(
// We load the state from the savedStateHandle for testing purposes. // We load the state from the savedStateHandle for testing purposes.
initialState = savedStateHandle[KEY_STATE] initialState = savedStateHandle[KEY_STATE]
@ -170,11 +167,7 @@ class VaultAddEditViewModel @Inject constructor(
// Set special conditions for autofill and fido2 save // Set special conditions for autofill and fido2 save
shouldShowCloseButton = autofillSaveItem == null && fido2AttestationOptions == null, shouldShowCloseButton = autofillSaveItem == null && fido2AttestationOptions == null,
shouldExitOnSave = shouldExitOnSave, shouldExitOnSave = shouldExitOnSave,
supportedItemTypes = getSupportedItemTypeOptions( supportedItemTypes = getSupportedItemTypeOptions(),
isSshKeyVaultItemSupported = featureFlagManager.getFeatureFlag(
key = FlagKey.SshKeyCipherItems,
),
),
) )
}, },
) { ) {
@ -216,11 +209,6 @@ class VaultAddEditViewModel @Inject constructor(
} }
.onEach(::sendAction) .onEach(::sendAction)
.launchIn(viewModelScope) .launchIn(viewModelScope)
featureFlagManager.getFeatureFlagFlow(FlagKey.SshKeyCipherItems)
.map { VaultAddEditAction.Internal.SshKeyCipherItemsFeatureFlagReceive(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
} }
override fun handleAction(action: VaultAddEditAction) { override fun handleAction(action: VaultAddEditAction) {
@ -1447,10 +1435,6 @@ class VaultAddEditViewModel @Inject constructor(
is VaultAddEditAction.Internal.ValidateFido2PinResultReceive -> { is VaultAddEditAction.Internal.ValidateFido2PinResultReceive -> {
handleValidateFido2PinResultReceive(action) handleValidateFido2PinResultReceive(action)
} }
is VaultAddEditAction.Internal.SshKeyCipherItemsFeatureFlagReceive -> {
handleSshKeyCipherItemsFeatureFlagReceive(action)
}
} }
} }
@ -1785,19 +1769,6 @@ class VaultAddEditViewModel @Inject constructor(
getRequestAndRegisterCredential() getRequestAndRegisterCredential()
} }
private fun handleSshKeyCipherItemsFeatureFlagReceive(
action: VaultAddEditAction.Internal.SshKeyCipherItemsFeatureFlagReceive,
) {
mutableStateFlow.update {
it.copy(
supportedItemTypes = getSupportedItemTypeOptions(
isSshKeyVaultItemSupported = action.enabled,
),
)
}
}
//endregion Internal Type Handlers //endregion Internal Type Handlers
//region Utility Functions //region Utility Functions
@ -3112,13 +3083,6 @@ sealed class VaultAddEditAction {
val generatorResult: GeneratorResult, val generatorResult: GeneratorResult,
) : Internal() ) : Internal()
/**
* Indicates that the the SSH key vault item feature flag state has been received.
*/
data class SshKeyCipherItemsFeatureFlagReceive(
val enabled: Boolean,
) : Internal()
/** /**
* Indicates that the vault item data has been received. * Indicates that the vault item data has been received.
*/ */
@ -3173,7 +3137,10 @@ sealed class VaultAddEditAction {
} }
} }
private fun getSupportedItemTypeOptions( /**
isSshKeyVaultItemSupported: Boolean, * Returns a list of item type options that can be selected during item creation.
) = VaultAddEditState.ItemTypeOption.entries *
.filter { isSshKeyVaultItemSupported || it != VaultAddEditState.ItemTypeOption.SSH_KEYS } * TODO: [PM-10413] Allow SSH key creation when the SDK supports it.
*/
private fun getSupportedItemTypeOptions() = VaultAddEditState.ItemTypeOption.entries
.filter { it != VaultAddEditState.ItemTypeOption.SSH_KEYS }

View file

@ -25,14 +25,12 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialRe
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
@ -155,15 +153,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
private val organizationEventManager = mockk<OrganizationEventManager> { private val organizationEventManager = mockk<OrganizationEventManager> {
every { trackEvent(event = any()) } just runs every { trackEvent(event = any()) } just runs
} }
private val mutableSshVaultItemsFeatureFlagFlow = MutableStateFlow<Boolean>(true)
private val featureFlagManager = mockk<FeatureFlagManager> {
every {
getFeatureFlagFlow(key = FlagKey.SshKeyCipherItems)
} returns mutableSshVaultItemsFeatureFlagFlow
every {
getFeatureFlag(key = FlagKey.SshKeyCipherItems)
} returns mutableSshVaultItemsFeatureFlagFlow.value
}
@BeforeEach @BeforeEach
fun setup() { fun setup() {
@ -180,6 +169,20 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
@Test @Test
fun `initial state should be correct when state is null`() = runTest { fun `initial state should be correct when state is null`() = runTest {
val expectedState = VaultAddEditState(
vaultAddEditType = VaultAddEditType.AddItem(VaultItemCipherType.LOGIN),
viewState = VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
isIndividualVaultDisabled = false,
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
),
dialog = null,
totpData = null,
shouldShowCloseButton = true,
shouldExitOnSave = false,
supportedItemTypes = VaultAddEditState.ItemTypeOption.entries
.filter { it != VaultAddEditState.ItemTypeOption.SSH_KEYS },
)
val viewModel = createAddVaultItemViewModel( val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState( savedStateHandle = createSavedStateHandleWithState(
state = null, state = null,
@ -188,10 +191,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
) )
viewModel.stateFlow.test { viewModel.stateFlow.test {
assertEquals( assertEquals(
createVaultAddItemState( expectedState,
commonContentViewState = VaultAddEditState.ViewState.Content.Common(),
typeContentViewState = createLoginTypeContentViewState(),
),
awaitItem(), awaitItem(),
) )
} }
@ -262,7 +262,8 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
type = VaultAddEditState.ViewState.Content.ItemType.Login(), type = VaultAddEditState.ViewState.Content.ItemType.Login(),
), ),
dialog = null, dialog = null,
supportedItemTypes = VaultAddEditState.ItemTypeOption.entries, supportedItemTypes = VaultAddEditState.ItemTypeOption.entries
.filter { it != VaultAddEditState.ItemTypeOption.SSH_KEYS },
), ),
viewModel.stateFlow.value, viewModel.stateFlow.value,
) )
@ -378,56 +379,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
} }
} }
@Test
fun `initial add state should be correct when SSH key feature flag is enabled`() {
mutableSshVaultItemsFeatureFlagFlow.value = true
val vaultAddEditType = VaultAddEditType.AddItem(VaultItemCipherType.LOGIN)
val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = initState,
vaultAddEditType = vaultAddEditType,
),
)
assertEquals(
initState,
viewModel.stateFlow.value,
)
}
@Test
fun `initial add state should be correct when SSH key feature flag is disabled`() {
mutableSshVaultItemsFeatureFlagFlow.value = false
every {
featureFlagManager.getFeatureFlag(key = FlagKey.SshKeyCipherItems)
} returns false
val vaultAddEditType = VaultAddEditType.AddItem(VaultItemCipherType.LOGIN)
val expectedState = VaultAddEditState(
vaultAddEditType = vaultAddEditType,
viewState = VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
isIndividualVaultDisabled = false,
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
),
dialog = null,
totpData = null,
shouldShowCloseButton = true,
shouldExitOnSave = false,
supportedItemTypes = VaultAddEditState.ItemTypeOption.entries
.filter { it != VaultAddEditState.ItemTypeOption.SSH_KEYS },
)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = null,
vaultAddEditType = vaultAddEditType,
),
)
assertEquals(
expectedState,
viewModel.stateFlow.value,
)
}
@Test @Test
fun `initial edit state should be correct`() = runTest { fun `initial edit state should be correct`() = runTest {
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID) val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
@ -3175,7 +3126,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
organizationEventManager = organizationEventManager, organizationEventManager = organizationEventManager,
featureFlagManager = featureFlagManager,
) )
} }
@ -4271,30 +4221,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
) )
} }
} }
@Suppress("MaxLineLength")
@Test
fun `SshKeyCipherItemsFeatureFlagReceive should update supportedItemTypes`() = runTest {
// Verify SSH keys is supported when feature flag is enabled.
viewModel.trySendAction(
VaultAddEditAction.Internal.SshKeyCipherItemsFeatureFlagReceive(enabled = true),
)
assertEquals(
VaultAddEditState.ItemTypeOption.entries,
viewModel.stateFlow.value.supportedItemTypes,
)
// Verify SSH keys is not supported when feature flag is disabled.
viewModel.trySendAction(
VaultAddEditAction.Internal.SshKeyCipherItemsFeatureFlagReceive(enabled = false),
)
assertEquals(
VaultAddEditState.ItemTypeOption.entries.filterNot {
it == VaultAddEditState.ItemTypeOption.SSH_KEYS
},
viewModel.stateFlow.value.supportedItemTypes,
)
}
} }
//region Helper functions //region Helper functions
@ -4429,7 +4355,6 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = bitwardenResourceManager, resourceManager = bitwardenResourceManager,
clock = clock, clock = clock,
organizationEventManager = organizationEventManager, organizationEventManager = organizationEventManager,
featureFlagManager = featureFlagManager,
) )
private fun createVaultData( private fun createVaultData(