mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-20 00:35:47 +01:00
Add screenlock and screenshot guards to SignalLoginViewDetails.
This commit is contained in:
committed by
Cody Henthorne
parent
b87e35fcef
commit
e61dd283c7
+15
-4
@@ -73,10 +73,16 @@ class AccountSettingsFragment : ComposeFragment() {
|
||||
val removalBiometrics = rememberBiometricsAuthentication(
|
||||
promptTitle = stringResource(AppSettingsR.string.AccountSettingsFragment__unlock_to_remove_two_factor_method),
|
||||
educationSheetMessage = stringResource(AppSettingsR.string.AccountSettingsFragment__to_remove_this_method_confirm_its_you),
|
||||
onAuthenticationFailed = { viewModel.onEvent(AccountSettingsEvent.MethodRemovalAuthenticationFailed) }
|
||||
onAuthenticationFailed = { viewModel.onEvent(AccountSettingsEvent.AuthenticationFailed) }
|
||||
)
|
||||
|
||||
CollectActions(viewModel.actions) { action -> handleAction(action, removalBiometrics) }
|
||||
val signalLoginBiometrics = rememberBiometricsAuthentication(
|
||||
promptTitle = stringResource(AppSettingsR.string.AccountSettingsFragment__unlock_to_view_signal_login),
|
||||
educationSheetMessage = stringResource(AppSettingsR.string.AccountSettingsFragment__to_view_your_signal_login_confirm_its_you),
|
||||
onAuthenticationFailed = { viewModel.onEvent(AccountSettingsEvent.AuthenticationFailed) }
|
||||
)
|
||||
|
||||
CollectActions(viewModel.actions) { action -> handleAction(action, removalBiometrics, signalLoginBiometrics) }
|
||||
|
||||
AccountSettingsScreen(
|
||||
state = state,
|
||||
@@ -84,12 +90,17 @@ class AccountSettingsFragment : ComposeFragment() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAction(action: AccountSettingsAction, removalBiometrics: BiometricsAuthentication) {
|
||||
private fun handleAction(action: AccountSettingsAction, removalBiometrics: BiometricsAuthentication, signalLoginBiometrics: BiometricsAuthentication) {
|
||||
when (action) {
|
||||
AccountSettingsAction.NavigateBack -> requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
AccountSettingsAction.LaunchCreatePinFlow -> pinFlowLauncher.launch(CreateSvrPinActivity.getIntentForPinCreate(requireContext()))
|
||||
AccountSettingsAction.LaunchChangePinFlow -> pinFlowLauncher.launch(CreateSvrPinActivity.getIntentForPinChangeFromSettings(requireContext()))
|
||||
AccountSettingsAction.ShowPinCreatedConfirmation -> Snackbar.make(requireView(), R.string.ConfirmKbsPinFragment__pin_created, Snackbar.LENGTH_LONG).show()
|
||||
AccountSettingsAction.AuthenticateToViewSignalLoginDetails -> {
|
||||
signalLoginBiometrics.withBiometricsAuthentication {
|
||||
viewModel.onEvent(AccountSettingsEvent.SignalLoginDetailsAuthenticated)
|
||||
}
|
||||
}
|
||||
AccountSettingsAction.NavigateToSignalLoginDetails -> findNavController().safeNavigate(R.id.action_accountSettingsFragment_to_signalLoginViewDetailsFragment)
|
||||
AccountSettingsAction.NavigateToTotpSetup -> findNavController().safeNavigate(R.id.action_accountSettingsFragment_to_authenticatorSetupFragment)
|
||||
is AccountSettingsAction.NavigateToRenameTotpApp -> {
|
||||
@@ -103,7 +114,7 @@ class AccountSettingsFragment : ComposeFragment() {
|
||||
viewModel.onEvent(AccountSettingsEvent.MethodRemovalAuthenticated(action.method))
|
||||
}
|
||||
}
|
||||
AccountSettingsAction.ShowRemovalAuthenticationFailed -> toast(AppSettingsR.string.AccountSettingsFragment__authentication_required)
|
||||
AccountSettingsAction.ShowAuthenticationFailed -> toast(AppSettingsR.string.AccountSettingsFragment__authentication_required)
|
||||
AccountSettingsAction.ShowTotpAppRemoved -> toast(AppSettingsR.string.AccountSettingsFragment__authenticator_app_removed)
|
||||
AccountSettingsAction.ShowTotpAppRemovalFailed -> toast(AppSettingsR.string.AccountSettingsFragment__couldnt_remove_authenticator_app)
|
||||
// TODO Open the two-factor authentication support article once one exists.
|
||||
|
||||
+5
-2
@@ -83,6 +83,9 @@ class AccountSettingsViewModel(
|
||||
applyRegistrationLockConfirmed()
|
||||
}
|
||||
AccountSettingsEvent.AccountAndRecoveryClicked -> {
|
||||
_actions.send(AccountSettingsAction.AuthenticateToViewSignalLoginDetails)
|
||||
}
|
||||
AccountSettingsEvent.SignalLoginDetailsAuthenticated -> {
|
||||
_actions.send(AccountSettingsAction.NavigateToSignalLoginDetails)
|
||||
}
|
||||
AccountSettingsEvent.AddTotpAppClicked -> {
|
||||
@@ -100,8 +103,8 @@ class AccountSettingsViewModel(
|
||||
is AccountSettingsEvent.MethodRemovalAuthenticated -> {
|
||||
applyMethodRemovalAuthenticated(event.method)
|
||||
}
|
||||
AccountSettingsEvent.MethodRemovalAuthenticationFailed -> {
|
||||
_actions.send(AccountSettingsAction.ShowRemovalAuthenticationFailed)
|
||||
AccountSettingsEvent.AuthenticationFailed -> {
|
||||
_actions.send(AccountSettingsAction.ShowAuthenticationFailed)
|
||||
}
|
||||
AccountSettingsEvent.RemoveTotpAppConfirmed -> {
|
||||
applyRemoveTotpAppConfirmed()
|
||||
|
||||
+3
@@ -21,6 +21,7 @@ import org.signal.core.util.Util
|
||||
import org.signal.passwordmanager.SignalCredentialManager
|
||||
import org.signal.signallogin.pdf.SignalLoginPdfRenderer
|
||||
import org.signal.signallogin.viewdetails.SignalLoginViewDetailsScreen
|
||||
import org.thoughtcrime.securesms.components.TemporaryScreenshotSecurity
|
||||
|
||||
/**
|
||||
* Shows the account and recovery keys that make up the user's Signal Login, the same way registration does.
|
||||
@@ -49,6 +50,8 @@ class SignalLoginViewDetailsFragment : ComposeFragment() {
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
TemporaryScreenshotSecurity.bind()
|
||||
|
||||
CollectActions(viewModel.actions) { action -> handleAction(action) }
|
||||
|
||||
SignalLoginViewDetailsScreen(
|
||||
|
||||
+16
-4
@@ -444,7 +444,7 @@ class AccountSettingsViewModelTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MethodRemovalAuthenticationFailed says so and removes nothing`() = runTest(testDispatcher) {
|
||||
fun `AuthenticationFailed says so and removes nothing`() = runTest(testDispatcher) {
|
||||
every { repository.isPhoneNumberless() } returns true
|
||||
coEvery { repository.getTwoFactorMethods() } returns methods(TOTP_APP)
|
||||
|
||||
@@ -452,9 +452,9 @@ class AccountSettingsViewModelTest {
|
||||
val actions = collectActions(viewModel.actions)
|
||||
|
||||
viewModel.onEvent(AccountSettingsEvent.RemoveMethodClicked(TOTP_APP))
|
||||
viewModel.onEvent(AccountSettingsEvent.MethodRemovalAuthenticationFailed)
|
||||
viewModel.onEvent(AccountSettingsEvent.AuthenticationFailed)
|
||||
|
||||
assertThat(actions.last()).isEqualTo(AccountSettingsAction.ShowRemovalAuthenticationFailed)
|
||||
assertThat(actions.last()).isEqualTo(AccountSettingsAction.ShowAuthenticationFailed)
|
||||
assertThat(viewModel.state.value.dialog).isEqualTo(Dialog.None)
|
||||
coVerify(exactly = 0) { repository.removeTotpApp(any()) }
|
||||
}
|
||||
@@ -546,7 +546,7 @@ class AccountSettingsViewModelTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AccountAndRecoveryClicked opens the Signal Login details screen`() = runTest(testDispatcher) {
|
||||
fun `AccountAndRecoveryClicked asks for the screen lock first`() = runTest(testDispatcher) {
|
||||
every { repository.isPhoneNumberless() } returns true
|
||||
|
||||
val viewModel = createViewModel()
|
||||
@@ -554,6 +554,18 @@ class AccountSettingsViewModelTest {
|
||||
|
||||
viewModel.onEvent(AccountSettingsEvent.AccountAndRecoveryClicked)
|
||||
|
||||
assertThat(actions.last()).isEqualTo(AccountSettingsAction.AuthenticateToViewSignalLoginDetails)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SignalLoginDetailsAuthenticated opens the Signal Login details screen`() = runTest(testDispatcher) {
|
||||
every { repository.isPhoneNumberless() } returns true
|
||||
|
||||
val viewModel = createViewModel()
|
||||
val actions = collectActions(viewModel.actions)
|
||||
|
||||
viewModel.onEvent(AccountSettingsEvent.SignalLoginDetailsAuthenticated)
|
||||
|
||||
assertThat(actions.last()).isEqualTo(AccountSettingsAction.NavigateToSignalLoginDetails)
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -27,6 +27,9 @@ sealed interface AccountSettingsAction {
|
||||
/** Tell the user their PIN was created. */
|
||||
data object ShowPinCreatedConfirmation : AccountSettingsAction
|
||||
|
||||
/** Ask the user to get past their screen lock before we show them the account and recovery keys. */
|
||||
data object AuthenticateToViewSignalLoginDetails : AccountSettingsAction
|
||||
|
||||
/** Open the screen that shows the account and recovery keys that make up the Signal Login. */
|
||||
data object NavigateToSignalLoginDetails : AccountSettingsAction
|
||||
|
||||
@@ -39,8 +42,8 @@ sealed interface AccountSettingsAction {
|
||||
/** Ask the user to get past their screen lock before we remove [method] from the account. */
|
||||
data class AuthenticateToRemoveMethod(val method: TwoFactorMethod) : AccountSettingsAction
|
||||
|
||||
/** Tell the user we couldn't confirm it was them, so nothing was removed. */
|
||||
data object ShowRemovalAuthenticationFailed : AccountSettingsAction
|
||||
/** Tell the user we couldn't confirm it was them, so whatever they asked for didn't happen. */
|
||||
data object ShowAuthenticationFailed : AccountSettingsAction
|
||||
|
||||
/** Tell the user their authenticator app was removed. */
|
||||
data object ShowTotpAppRemoved : AccountSettingsAction
|
||||
|
||||
+6
-3
@@ -16,9 +16,12 @@ sealed interface AccountSettingsEvent {
|
||||
/** The user tapped the navigation (back) icon. */
|
||||
data object NavigateBackClicked : AccountSettingsEvent
|
||||
|
||||
/** The user tapped the Signal Login card, which shows the account and recovery keys. */
|
||||
/** The user tapped the Signal Login card, which asks for the screen lock before showing the account and recovery keys. */
|
||||
data object AccountAndRecoveryClicked : AccountSettingsEvent
|
||||
|
||||
/** The user got past their screen lock, so we can show them the account and recovery keys. */
|
||||
data object SignalLoginDetailsAuthenticated : AccountSettingsEvent
|
||||
|
||||
/** The user tapped the row that either creates or changes their PIN. */
|
||||
data object ModifyPinClicked : AccountSettingsEvent
|
||||
|
||||
@@ -60,8 +63,8 @@ sealed interface AccountSettingsEvent {
|
||||
/** The user got past their screen lock, so we can go on asking them to confirm removing [method]. */
|
||||
data class MethodRemovalAuthenticated(val method: TwoFactorMethod) : AccountSettingsEvent
|
||||
|
||||
/** The screen lock turned the user away, so [RemoveMethodClicked] goes no further. */
|
||||
data object MethodRemovalAuthenticationFailed : AccountSettingsEvent
|
||||
/** The screen lock turned the user away, so whatever asked for it goes no further. */
|
||||
data object AuthenticationFailed : AccountSettingsEvent
|
||||
|
||||
/** The user confirmed removing the authenticator app named by the open dialog, which removes it. */
|
||||
data object RemoveTotpAppConfirmed : AccountSettingsEvent
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
<string name="AccountSettingsFragment__unlock_to_remove_two_factor_method">Unlock to remove two-factor method</string>
|
||||
<!-- Title of the sheet explaining that the device screen lock is needed before a two-factor authentication method can be removed -->
|
||||
<string name="AccountSettingsFragment__to_remove_this_method_confirm_its_you">To remove this method, confirm it\'s you</string>
|
||||
<!-- Toast shown when the device screen lock wasn\'t confirmed, so nothing was removed -->
|
||||
<!-- Title of the device screen lock prompt shown before the Signal Login details can be viewed -->
|
||||
<string name="AccountSettingsFragment__unlock_to_view_signal_login">Unlock to view Signal Login</string>
|
||||
<!-- Title of the sheet explaining that the device screen lock is needed before the Signal Login details can be viewed -->
|
||||
<string name="AccountSettingsFragment__to_view_your_signal_login_confirm_its_you">To view your Signal Login, confirm it\'s you</string>
|
||||
<!-- Toast shown when the device screen lock wasn\'t confirmed, so nothing happened -->
|
||||
<string name="AccountSettingsFragment__authentication_required">Authentication required</string>
|
||||
<!-- Title of the dialog shown when the account already has as many authenticator apps as it\'s allowed -->
|
||||
<string name="AccountSettingsFragment__cant_add_authenticator_app">Can\'t add authenticator app</string>
|
||||
|
||||
Reference in New Issue
Block a user