PM-24089: Remove Mutual TLS feature flag (#5566)

This commit is contained in:
David Perez
2025-07-24 08:33:32 -05:00
committed by GitHub
parent c7df80ff00
commit bb950c8c59
9 changed files with 48 additions and 112 deletions

View File

@@ -25,7 +25,6 @@ sealed class FlagKey<out T : Any> {
ImportLoginsFlow,
CredentialExchangeProtocolImport,
CredentialExchangeProtocolExport,
MutualTls,
SingleTapPasskeyCreation,
SingleTapPasskeyAuthentication,
AnonAddySelfHostAlias,
@@ -80,14 +79,6 @@ sealed class FlagKey<out T : Any> {
override val defaultValue: Boolean = false
}
/**
* Data object holding the feature flag key for the Mutual TLS feature.
*/
data object MutualTls : FlagKey<Boolean>() {
override val keyName: String = "mutual-tls"
override val defaultValue: Boolean = false
}
/**
* Data object holding the feature flag key to enable single tap passkey creation.
*/

View File

@@ -323,59 +323,58 @@ fun EnvironmentScreen(
.standardHorizontalMargin(),
)
if (state.showMutualTlsOptions) {
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.client_certificate_mtls),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.client_certificate_mtls),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenTextField(
label = stringResource(id = R.string.certificate_alias),
value = state.keyAlias,
supportingText = stringResource(
id = R.string.certificate_used_for_client_authentication,
),
onValueChange = {},
readOnly = true,
cardStyle = CardStyle.Full,
textFieldTestTag = "KeyAliasEntry",
modifier = Modifier
.fillMaxWidth()
.focusProperties { canFocus = false }
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenTextField(
label = stringResource(id = R.string.certificate_alias),
value = state.keyAlias,
supportingText = stringResource(
id = R.string.certificate_used_for_client_authentication,
),
onValueChange = {},
readOnly = true,
cardStyle = CardStyle.Full,
textFieldTestTag = "KeyAliasEntry",
modifier = Modifier
.fillMaxWidth()
.focusProperties { canFocus = false }
.standardHorizontalMargin(),
)
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenFilledButton(
label = stringResource(id = R.string.import_certificate),
onClick = remember(viewModel) {
{ viewModel.trySendAction(EnvironmentAction.ImportCertificateClick) }
},
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.testTag("ImportCertificateButton"),
)
BitwardenFilledButton(
label = stringResource(id = R.string.import_certificate),
onClick = remember(viewModel) {
{ viewModel.trySendAction(EnvironmentAction.ImportCertificateClick) }
},
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.testTag("ImportCertificateButton"),
)
Spacer(modifier = Modifier.height(height = 12.dp))
Spacer(modifier = Modifier.height(height = 12.dp))
BitwardenOutlinedButton(
label = stringResource(id = R.string.choose_system_certificate),
onClick = remember(viewModel) {
{ viewModel.trySendAction(EnvironmentAction.ChooseSystemCertificateClick) }
},
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.testTag("ChooseSystemCertificateButton"),
)
BitwardenOutlinedButton(
label = stringResource(id = R.string.choose_system_certificate),
onClick = remember(viewModel) {
{ viewModel.trySendAction(EnvironmentAction.ChooseSystemCertificateClick) }
},
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.testTag("ChooseSystemCertificateButton"),
)
}
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}

View File

@@ -16,8 +16,6 @@ import com.bitwarden.ui.util.asText
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.datasource.disk.model.MutualTlsKeyHost
import com.x8bit.bitwarden.data.platform.manager.CertificateManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.model.ImportPrivateKeyResult
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.vault.manager.FileManager
@@ -28,7 +26,6 @@ import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -46,7 +43,6 @@ class EnvironmentViewModel @Inject constructor(
private val environmentRepository: EnvironmentRepository,
private val fileManager: FileManager,
private val certificateManager: CertificateManager,
private val featureFlagManager: FeatureFlagManager,
private val snackbarRelayManager: SnackbarRelayManager,
private val savedStateHandle: SavedStateHandle,
) : BaseViewModel<EnvironmentState, EnvironmentEvent, EnvironmentAction>(
@@ -70,21 +66,13 @@ class EnvironmentViewModel @Inject constructor(
keyAlias = keyAlias,
keyHost = keyHost,
dialog = null,
showMutualTlsOptions = featureFlagManager.getFeatureFlag(FlagKey.MutualTls),
)
},
) {
init {
stateFlow
.onEach {
savedStateHandle[KEY_STATE] = it
}
.launchIn(viewModelScope)
featureFlagManager.getFeatureFlagFlow(FlagKey.MutualTls)
.map { EnvironmentAction.Internal.MutualTlsFeatureFlagUpdate(it) }
.onEach(::handleAction)
.onEach { savedStateHandle[KEY_STATE] = it }
.launchIn(viewModelScope)
}
@@ -275,10 +263,6 @@ class EnvironmentViewModel @Inject constructor(
is EnvironmentAction.Internal.ImportKeyResultReceive -> {
handleSaveKeyResultReceive(action)
}
is EnvironmentAction.Internal.MutualTlsFeatureFlagUpdate -> {
handleMutualTlsFeatureFlagUpdate(action)
}
}
}
@@ -361,16 +345,6 @@ class EnvironmentViewModel @Inject constructor(
}
}
private fun handleMutualTlsFeatureFlagUpdate(
action: EnvironmentAction.Internal.MutualTlsFeatureFlagUpdate,
) {
mutableStateFlow.update {
it.copy(
showMutualTlsOptions = action.enabled,
)
}
}
private fun handleChooseSystemCertificateClickAction() {
mutableStateFlow.update {
it.copy(
@@ -472,7 +446,6 @@ data class EnvironmentState(
val iconsServerUrl: String,
val keyAlias: String,
val dialog: DialogState?,
val showMutualTlsOptions: Boolean,
// internal
private val keyHost: MutualTlsKeyHost?,
) : Parcelable {
@@ -692,12 +665,5 @@ sealed class EnvironmentAction {
data class ImportKeyResultReceive(
val result: ImportPrivateKeyResult,
) : Internal()
/**
* Indicates the mutual TLS feature flag was updated.
*/
data class MutualTlsFeatureFlagUpdate(
val enabled: Boolean,
) : Internal()
}
}

View File

@@ -30,7 +30,6 @@ fun <T : Any> FlagKey<T>.ListItemContent(
FlagKey.CredentialExchangeProtocolImport,
FlagKey.CredentialExchangeProtocolExport,
FlagKey.CipherKeyEncryption,
FlagKey.MutualTls,
FlagKey.SingleTapPasskeyCreation,
FlagKey.SingleTapPasskeyAuthentication,
FlagKey.AnonAddySelfHostAlias,
@@ -85,7 +84,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
FlagKey.CredentialExchangeProtocolImport -> stringResource(R.string.cxp_import)
FlagKey.CredentialExchangeProtocolExport -> stringResource(R.string.cxp_export)
FlagKey.CipherKeyEncryption -> stringResource(R.string.cipher_key_encryption)
FlagKey.MutualTls -> stringResource(R.string.mutual_tls)
FlagKey.SingleTapPasskeyCreation -> stringResource(R.string.single_tap_passkey_creation)
FlagKey.SingleTapPasskeyAuthentication -> {
stringResource(R.string.single_tap_passkey_authentication)

View File

@@ -865,7 +865,6 @@ Do you want to switch to this account?</string>
<string name="you_ll_only_need_to_set_up_authenticator_key">Youll only need to set up Authenticator Key for logins that require two-factor authentication with a code. The key will continuously generate six-digit codes you can use to log in.</string>
<string name="coachmark_3_of_3">3 OF 3</string>
<string name="you_must_add_a_web_address_to_use_autofill_to_access_this_account">You must add a web address to use autofill to access this account.</string>
<string name="mutual_tls">Mutual TLS</string>
<string name="single_tap_passkey_creation">Single tap passkey creation</string>
<string name="single_tap_passkey_authentication">Single tap passkey sign-on</string>
<string name="learn_about_new_logins">Learn about new logins</string>

View File

@@ -37,10 +37,6 @@ class FlagKeyTest {
FlagKey.SingleTapPasskeyAuthentication.keyName,
"single-tap-passkey-authentication",
)
assertEquals(
FlagKey.MutualTls.keyName,
"mutual-tls",
)
assertEquals(
FlagKey.AnonAddySelfHostAlias.keyName,
"anon-addy-self-host-alias",

View File

@@ -437,7 +437,6 @@ class EnvironmentScreenTest : BitwardenComposeTest() {
iconsServerUrl = "",
keyHost = null,
dialog = null,
showMutualTlsOptions = true,
)
}
}

View File

@@ -11,8 +11,6 @@ import com.bitwarden.ui.util.asText
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.datasource.disk.model.MutualTlsKeyHost
import com.x8bit.bitwarden.data.platform.manager.CertificateManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.model.ImportPrivateKeyResult
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
import com.x8bit.bitwarden.data.vault.manager.FileManager
@@ -28,7 +26,6 @@ import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
@@ -37,11 +34,6 @@ import org.junit.jupiter.api.Test
class EnvironmentViewModelTest : BaseViewModelTest() {
private val fakeEnvironmentRepository = FakeEnvironmentRepository()
private val mutableMutualTlsFeatureFlagFlow = MutableStateFlow(true)
private val mockFeatureFlagManager = mockk<FeatureFlagManager> {
every { getFeatureFlag(FlagKey.MutualTls) } returns true
every { getFeatureFlagFlow(FlagKey.MutualTls) } returns mutableMutualTlsFeatureFlagFlow
}
private val mockCertificateManager = mockk<CertificateManager> {
every { getMutualTlsKeyAliases() } returns emptyList()
}
@@ -814,7 +806,6 @@ class EnvironmentViewModelTest : BaseViewModelTest() {
): EnvironmentViewModel =
EnvironmentViewModel(
environmentRepository = fakeEnvironmentRepository,
featureFlagManager = mockFeatureFlagManager,
certificateManager = mockCertificateManager,
fileManager = mockFileManager,
snackbarRelayManager = snackbarRelayManager,
@@ -833,7 +824,6 @@ class EnvironmentViewModelTest : BaseViewModelTest() {
iconsServerUrl = "",
keyHost = null,
dialog = null,
showMutualTlsOptions = true,
)
}
}

View File

@@ -148,7 +148,6 @@ private val DEFAULT_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf
FlagKey.ImportLoginsFlow to true,
FlagKey.CredentialExchangeProtocolImport to true,
FlagKey.CredentialExchangeProtocolExport to true,
FlagKey.MutualTls to true,
FlagKey.SingleTapPasskeyCreation to true,
FlagKey.SingleTapPasskeyAuthentication to true,
FlagKey.AnonAddySelfHostAlias to true,
@@ -164,7 +163,6 @@ private val UPDATED_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf
FlagKey.ImportLoginsFlow to false,
FlagKey.CredentialExchangeProtocolImport to false,
FlagKey.CredentialExchangeProtocolExport to false,
FlagKey.MutualTls to false,
FlagKey.SingleTapPasskeyCreation to false,
FlagKey.SingleTapPasskeyAuthentication to false,
FlagKey.AnonAddySelfHostAlias to false,