From 8788fe6ec5b6b408ff1d86e45270ba27928ab872 Mon Sep 17 00:00:00 2001 From: KremeCN <3265494849@qq.com> Date: Sun, 23 Aug 2026 23:45:39 +0800 Subject: [PATCH] fix: secure login route lifecycle --- .../flagship/abox/manager/ui/login/LoginScreen.kt | 12 ++++++++++++ .../session/profile/ProviderProfileStoreImpl.kt | 6 +++--- .../session/ProviderProfileStoreFailureTest.kt | 3 +++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/login/LoginScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/login/LoginScreen.kt index 8cf7f47..daae812 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/login/LoginScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/login/LoginScreen.kt @@ -21,13 +21,17 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -47,6 +51,9 @@ fun LoginRoute( viewModel: LoginViewModel = hiltViewModel(), ) { LaunchedEffect(profile.id) { viewModel.loadProfile(profile) } + DisposableEffect(profile.id) { + onDispose(viewModel::clearSensitiveState) + } val state = viewModel.uiState.collectAsStateWithLifecycle().value LoginScreen( profile = profile, @@ -146,6 +153,11 @@ fun LoginScreen( onValueChange = onPasswordChange, label = { Text(stringResource(R.string.login_password)) }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + autoCorrect = false, + ), enabled = formEnabled, singleLine = true, modifier = Modifier.fillMaxWidth(), diff --git a/android/core/session/src/main/kotlin/com/flagship/abox/manager/session/profile/ProviderProfileStoreImpl.kt b/android/core/session/src/main/kotlin/com/flagship/abox/manager/session/profile/ProviderProfileStoreImpl.kt index c326082..23ac9e9 100644 --- a/android/core/session/src/main/kotlin/com/flagship/abox/manager/session/profile/ProviderProfileStoreImpl.kt +++ b/android/core/session/src/main/kotlin/com/flagship/abox/manager/session/profile/ProviderProfileStoreImpl.kt @@ -43,12 +43,12 @@ class ProviderProfileStoreImpl( override fun observeLoginPreferences(profileId: ProviderProfileId): Flow = safeData.map { preferencesFrom(it, profileId) } - override suspend fun profiles(): List = profilesFrom(dataStore.data.first()) + override suspend fun profiles(): List = profilesFrom(safeData.first()) - override suspend fun activeProfile(): ProviderProfile? = activeProfileFrom(dataStore.data.first()) + override suspend fun activeProfile(): ProviderProfile? = activeProfileFrom(safeData.first()) override suspend fun loginPreferences(profileId: ProviderProfileId): LoginPreferences = - preferencesFrom(dataStore.data.first(), profileId) + preferencesFrom(safeData.first(), profileId) override suspend fun saveProfile(profile: ProviderProfile): DomainResult = operation { dataStore.edit { preferences -> diff --git a/android/core/session/src/test/kotlin/com/flagship/abox/manager/session/ProviderProfileStoreFailureTest.kt b/android/core/session/src/test/kotlin/com/flagship/abox/manager/session/ProviderProfileStoreFailureTest.kt index 011ca53..779e572 100644 --- a/android/core/session/src/test/kotlin/com/flagship/abox/manager/session/ProviderProfileStoreFailureTest.kt +++ b/android/core/session/src/test/kotlin/com/flagship/abox/manager/session/ProviderProfileStoreFailureTest.kt @@ -19,6 +19,9 @@ class ProviderProfileStoreFailureTest { assertEquals(emptyList(), store.observeProfiles().first()) assertEquals(null, store.observeActiveProfile().first()) + assertEquals(emptyList(), store.profiles()) + assertEquals(null, store.activeProfile()) + assertEquals(false, store.loginPreferences(com.flagship.abox.manager.domain.ProviderProfileId("profile")).autoLoginEnabled) } private class FailingReadDataStore : DataStore {