From 0fbaec1263e6275778335322c882cffb5a298d0f Mon Sep 17 00:00:00 2001 From: Shinokuni Date: Fri, 10 May 2024 14:02:45 +0200 Subject: [PATCH] Put login and password in encrypted shared preferences --- appcompose/build.gradle | 2 ++ .../readrops/app/compose/ComposeAppModule.kt | 17 +++++++++++++++++ .../credentials/AccountCredentialsScreen.kt | 4 ++-- .../AccountCredentialsScreenModel.kt | 9 ++++++++- .../readrops/app/compose/base/TabScreenModel.kt | 12 ++++++++++++ gradle/libs.versions.toml | 2 ++ 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/appcompose/build.gradle b/appcompose/build.gradle index 2a0041a4..61483969 100644 --- a/appcompose/build.gradle +++ b/appcompose/build.gradle @@ -91,4 +91,6 @@ dependencies { androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0' coreLibraryDesugaring(libs.jdk.desugar) + + implementation(libs.encrypted.preferences) } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/ComposeAppModule.kt b/appcompose/src/main/java/com/readrops/app/compose/ComposeAppModule.kt index 7d8cabf4..b2aac5fa 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/ComposeAppModule.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/ComposeAppModule.kt @@ -1,5 +1,7 @@ package com.readrops.app.compose +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey import com.readrops.api.services.Credentials import com.readrops.app.compose.account.AccountScreenModel import com.readrops.app.compose.account.credentials.AccountCredentialsScreenModel @@ -13,6 +15,7 @@ import com.readrops.app.compose.repositories.LocalRSSRepository import com.readrops.app.compose.timelime.TimelineScreenModel import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.AccountType +import org.koin.android.ext.koin.androidContext import org.koin.core.parameter.parametersOf import org.koin.dsl.module @@ -44,4 +47,18 @@ val composeAppModule = module { else -> throw IllegalArgumentException("Unknown account type") } } + + single { + val masterKey = MasterKey.Builder(androidContext()) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + EncryptedSharedPreferences.create( + androidContext(), + "account_credentials", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + } } \ No newline at end of file diff --git a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt index cd569ff2..18a6c15a 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreen.kt @@ -36,8 +36,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import com.readrops.app.compose.R import com.readrops.app.compose.home.HomeScreen -import com.readrops.app.compose.util.components.AndroidScreen import com.readrops.app.compose.util.ErrorMessage +import com.readrops.app.compose.util.components.AndroidScreen import com.readrops.app.compose.util.theme.ShortSpacer import com.readrops.app.compose.util.theme.VeryLargeSpacer import com.readrops.app.compose.util.theme.spacing @@ -118,7 +118,7 @@ class AccountCredentialsScreen( label = { Text(text = stringResource(id = R.string.login)) }, singleLine = true, isError = state.isLoginError, - supportingText = { Text(text = state.passwordError?.errorText().orEmpty()) }, + supportingText = { Text(text = state.loginError?.errorText().orEmpty()) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), modifier = Modifier.fillMaxWidth() ) diff --git a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreenModel.kt b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreenModel.kt index eec68d6e..8d93c028 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreenModel.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/account/credentials/AccountCredentialsScreenModel.kt @@ -1,5 +1,6 @@ package com.readrops.app.compose.account.credentials +import android.content.SharedPreferences import android.util.Patterns import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope @@ -72,7 +73,13 @@ class AccountCredentialsScreenModel( return@launch } - database.newAccountDao().insert(account) + account.id = database.newAccountDao().insert(account).toInt() + + get().edit() + .putString(account.loginKey, account.login) + .putString(account.passwordKey, account.password) + .apply() + mutableState.update { it.copy(goToHomeScreen = true) } } } diff --git a/appcompose/src/main/java/com/readrops/app/compose/base/TabScreenModel.kt b/appcompose/src/main/java/com/readrops/app/compose/base/TabScreenModel.kt index 3d94cc08..f7a22e07 100644 --- a/appcompose/src/main/java/com/readrops/app/compose/base/TabScreenModel.kt +++ b/appcompose/src/main/java/com/readrops/app/compose/base/TabScreenModel.kt @@ -1,7 +1,10 @@ package com.readrops.app.compose.base +import android.content.SharedPreferences import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.screenModelScope +import com.readrops.api.services.Credentials +import com.readrops.api.utils.AuthInterceptor import com.readrops.app.compose.repositories.BaseRepository import com.readrops.db.Database import com.readrops.db.entities.account.Account @@ -35,8 +38,17 @@ abstract class TabScreenModel( .distinctUntilChanged() .collect { account -> if (account != null) { + if (account.login == null || account.password == null) { + val encryptedPreferences = get() + + account.login = encryptedPreferences.getString(account.loginKey, null) + account.password = encryptedPreferences.getString(account.passwordKey, null) + } + currentAccount = account repository = get(parameters = { parametersOf(account) }) + // very important to avoid credentials conflicts between accounts + get().credentials = Credentials.toCredentials(account) accountEvent.emit(account) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 67d1ecb6..f125d922 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,6 +65,8 @@ kotlinxmlbuilder = "org.redundent:kotlin-xml-builder:1.7.3" #TODO update this jdk-desugar = "com.android.tools:desugar_jdk_libs:2.0.4" +encrypted-preferences = "androidx.security:security-crypto:1.1.0-alpha06" + [bundles] compose = ["bom", "compose-foundation", "compose-runtime", "compose-animation", "compose-ui", "compose-ui-tooling", "compose-ui-tooling-preview", "compose-material3"]