Skip to content

Commit

Permalink
fix: remove kvault
Browse files Browse the repository at this point in the history
replace it with a base64 encoded password in the database for now
i know it's a bad practice.
I will be implementing an encrypted key-value storage solution later with EncryptedSharedPreferences.
this is a hotfix for the many people that can't access the application because of this oversight.
  • Loading branch information
abdallahmehiz committed Dec 15, 2024
1 parent ce3089c commit 5fa083f
Show file tree
Hide file tree
Showing 18 changed files with 45 additions and 46 deletions.
1 change: 0 additions & 1 deletion composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ kotlin {

implementation(libs.kotlinx.datetime)

implementation(libs.kvault)
api(libs.bundles.koin)
api(libs.bundles.datastore)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package mehiz.abdallah.progres

import android.app.Application
import com.google.firebase.FirebaseApp
import com.liftric.kvault.KVault
import di.initKoin
import mehiz.abdallah.progres.di.WorkersModule
import org.koin.android.ext.koin.androidContext
Expand All @@ -22,7 +21,6 @@ class App : Application(), KoinComponent {
initKoin(
datastorePath = filesDir.path,
credentialManager = CredentialManager(applicationContext),
kVault = KVault(applicationContext),
platformUtils = PlatformUtils(applicationContext)
),
WorkersModule,
Expand Down
13 changes: 6 additions & 7 deletions composeApp/src/androidMain/kotlin/utils/AuthRefreshWorker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.kizitonwose.calendar.core.minusDays
import com.liftric.kvault.KVault
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import mehiz.abdallah.progres.core.TAG
import mehiz.abdallah.progres.domain.UserAuthUseCase
import preferences.KVaultKeys
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi

class AuthRefreshWorker(
appContext: Context,
workerParams: WorkerParameters,
private val authUseCase: UserAuthUseCase,
private val kVault: KVault,
) : CoroutineWorker(appContext, workerParams) {

@OptIn(ExperimentalEncodingApi::class)
override suspend fun doWork(): Result {
Log.d(TAG, "Token refresh worker started")
val timeZone = TimeZone.currentSystemDefault()
Expand All @@ -35,10 +35,9 @@ class AuthRefreshWorker(
)
Result.retry()
} else {
val id = kVault.string(KVaultKeys.id)
checkNotNull(id) { "Vault doesn't contain id" }
val password = kVault.string(KVaultKeys.password)
checkNotNull(password) { "Vault doesn't contain password" }
val id = authUseCase.getUsername()
val password = Base64.decode(authUseCase.getPassword() ?: throw Exception("User credentials unavailable"))
.toString()
authUseCase.refreshLogin(id, password)
Log.d(TAG, "Token refreshed successfully")
Result.success()
Expand Down
5 changes: 1 addition & 4 deletions composeApp/src/commonMain/kotlin/di/ApplicationModule.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package di

import com.liftric.kvault.KVault
import org.koin.core.module.Module
import org.koin.dsl.module
import utils.CredentialManager
import utils.PlatformUtils

val ApplicationModule: (
credentialManger: CredentialManager,
kvault: KVault,
platformUtils: PlatformUtils
) -> Module = { credentialManager, kvault, platformUtils ->
) -> Module = { credentialManager, platformUtils ->
module {
single { platformUtils }
single { credentialManager }
single { kvault }
}
}
4 changes: 1 addition & 3 deletions composeApp/src/commonMain/kotlin/di/InitKoin.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package di

import com.liftric.kvault.KVault
import mehiz.abdallah.progres.domain.di.DomainModule
import org.koin.dsl.module
import utils.CredentialManager
Expand All @@ -9,13 +8,12 @@ import utils.PlatformUtils
fun initKoin(
datastorePath: String,
credentialManager: CredentialManager,
kVault: KVault,
platformUtils: PlatformUtils,
) = module {
includes(
PreferencesModule(datastorePath),
DomainModule,
ScreenModelsModule,
ApplicationModule(credentialManager, kVault, platformUtils)
ApplicationModule(credentialManager, platformUtils)
)
}
2 changes: 1 addition & 1 deletion composeApp/src/commonMain/kotlin/ui/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ object HomeScreen : Screen {
modifier = Modifier
.then(if (nextSchedule != null) Modifier.revealable(nextSchedule.id, revealState) else Modifier)
.clickable(
onClick = { scope.launch { revealState.reveal(nextSchedule!!.id) } },
onClick = { nextSchedule?.let { scope.launch { revealState.reveal(it.id) } } },
interactionSource = remember { MutableInteractionSource() },
indication = null,
),
Expand Down
9 changes: 2 additions & 7 deletions composeApp/src/commonMain/kotlin/ui/home/HomeScreenModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ui.home

import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.liftric.kvault.KVault
import io.github.aakira.napier.Napier
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
Expand All @@ -23,7 +22,6 @@ import mehiz.abdallah.progres.domain.models.AccommodationModel
import mehiz.abdallah.progres.domain.models.BacInfoModel
import mehiz.abdallah.progres.domain.models.StudentCardModel
import mehiz.abdallah.progres.domain.models.SubjectScheduleModel
import preferences.KVaultKeys
import presentation.utils.RequestState

data class HomeScreenUIData(
Expand All @@ -38,7 +36,6 @@ class HomeScreenModel(
private val subjectScheduleUseCase: SubjectScheduleUseCase,
private val accommodationUseCase: AccommodationUseCase,
private val authUseCase: UserAuthUseCase,
private val kVault: KVault,
) : ScreenModel {

private val _data = MutableStateFlow<RequestState<HomeScreenUIData>>(RequestState.Loading)
Expand Down Expand Up @@ -112,10 +109,8 @@ class HomeScreenModel(
suspend fun tryRefreshToken() {
val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
if (authUseCase.getExpirationDate().date.toEpochDays() < now.date.toEpochDays()) {
val id = kVault.string(KVaultKeys.id)
checkNotNull(id) { "Vault doesn't contain id" }
val password = kVault.string(KVaultKeys.password)
checkNotNull(password) { "Vault doesn't contain password" }
val id = authUseCase.getUsername()
val password = authUseCase.getPassword() ?: throw Exception("User credentials unavailable")
authUseCase.refreshLogin(id, password)
}
}
Expand Down
9 changes: 2 additions & 7 deletions composeApp/src/commonMain/kotlin/ui/onboarding/LoginScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import com.dokar.sonner.Toast
import com.dokar.sonner.ToastType
import com.dokar.sonner.ToasterState
import com.liftric.kvault.KVault
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.DelicateCoroutinesApi
Expand All @@ -76,7 +75,6 @@ import mehiz.abdallah.progres.i18n.MR
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.koinInject
import preferences.BasePreferences
import preferences.KVaultKeys
import preferences.Language
import preferences.preference.collectAsState
import presentation.WheelNumberPicker
Expand All @@ -94,16 +92,13 @@ object LoginScreen : Screen {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val kVault = koinInject<KVault>()
val accountUseCase = koinInject<UserAuthUseCase>()
val authUseCase = koinInject<UserAuthUseCase>()
val basePreferences = koinInject<BasePreferences>()

LoginScreen(
onLoginPressed = { id, password ->
accountUseCase.login(id, password)
authUseCase.login(id, password)
basePreferences.isLoggedIn.set(true)
kVault.set(KVaultKeys.id, id)
kVault.set(KVaultKeys.password, password)
navigator.replaceAll(HomeScreen)
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import com.alorma.compose.settings.ui.SettingsMenuLink
import com.alorma.compose.settings.ui.SettingsSwitch
import com.liftric.kvault.KVault
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
Expand Down Expand Up @@ -133,7 +132,6 @@ object PreferencesScreen : Screen {
}
}
val accountUseCase = koinInject<AccountUseCase>()
val kVault = koinInject<KVault>()
SettingsMenuLink(
title = { Text(stringResource(MR.strings.pref_logout)) },
subtitle = { Text(stringResource(MR.strings.pref_logout_subtitle)) },
Expand All @@ -142,7 +140,6 @@ object PreferencesScreen : Screen {
scope.launch(Dispatchers.IO) {
accountUseCase.logout()
preferences.isLoggedIn.set(false)
kVault.clear()
navigator.replaceAll(LoginScreen)
}
},
Expand Down
4 changes: 2 additions & 2 deletions docs/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ <h2>Disclaimer</h2>
<h3>This App, is an unofficial port of the <a href="https://play.google.com/store/apps/details?id=app.progres.webetu">Progres WebEtu</a> Mobile app. thus we (the developer(s)) don't have access to almost any data collected and/or stored by the Progres APIs made by DRDN.</h3>
<h2>Data collection</h2>
<h3>Other than any data collected by the already mentioned Progres APIs used, the app the only collects crash logs, performance metrics and usage analytics.</h3>
<h3>One note being that the app, for refreshing user tokens and provide a better experience, does save the user's id and password in an encrypted manner in it's files directory.</h3>
<h3>One note being that the app, for refreshing user tokens and provide a better experience, does save the user's id and password in an encoded manner in its Database. This is subject to change in the future.</h3>
<h3>If you want to check how the data coming from the official Progres APIs is being processed on your device, check the app's source code <a href="https://github.com/abdallahmehiz/progres">here</a>.</h3>
<h2>Changes to this policy</h2>
<h3>While we currently do not collect any data, we hold the right to change this policy at any time only for the purpose of providing a better user experience.</h3>
<h3>Users who are already using this app will be notified in-app of such updates and are encouraged to revisit this page regularly to check for any changes.</h3>
<h2>Contact us</h2>
<h3>If you have any questions or concerns about this Privacy Policy, please contact us at <a href="mailto:[email protected]">[email protected]</a></h3>
<h2>Effective Date</h2>
<h3>This Privacy Policy is effective as of Oct 12th 2024.</h3>
<h3>This Privacy Policy is effective as of Oct 15th 2024.</h3>
<h3>By using Progres, you acknowledge that you have read and understood this Privacy Policy.</h3>
</body>
</html>
1 change: 1 addition & 0 deletions domain/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ sqldelight {
databases {
create("ProgresDB") {
packageName.set("mehiz.abdallah.progres.data.db")
schemaOutputDirectory = file("src/commonMain/sqldelight/databases")
verifyMigrations = true
verifyDefinitions = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class UserAuthDao(
userId = userId,
uuid = uuid,
establishmentId = establishmentId,
userName = userName
userName = userName,
password64 = password64
)
}
}
Expand Down Expand Up @@ -49,6 +50,10 @@ class UserAuthDao(
return queries.getEstablishmentId().executeAsOne()
}

suspend fun getBase64EncodedPassword(): String? {
return queries.getBase64EncodedPassword().executeAsOneOrNull()?.password64
}

suspend fun deleteUserAuth() {
queries.deleteUserAuth()
}
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ expirationDate TEXT NOT NULL,
userId INTEGER NOT NULL,
uuid TEXT NOT NULL,
establishmentId INTEGER NOT NULL,
userName TEXT NOT NULL
userName TEXT NOT NULL,
password64 TEXT
);

insert:
INSERT OR REPLACE INTO UserAuthTable(individualId, token, expirationDate, userId, uuid, establishmentId, userName) VALUES (?, ?, ?, ?, ?, ?, ?);
INSERT OR REPLACE INTO UserAuthTable(individualId, token, expirationDate, userId, uuid, establishmentId, userName, password64) VALUES (?, ?, ?, ?, ?, ?, ?, ?);

getStudentUuid:
SELECT uuid FROM UserAuthTable LIMIT 1;
Expand All @@ -29,5 +30,8 @@ SELECT userName FROM UserAuthTable LIMIT 1;
getEstablishmentId:
SELECT establishmentId FROM UserAuthTable LIMIT 1;

getBase64EncodedPassword:
SELECT password64 FROM UserAuthTable LIMIT 1;

deleteUserAuth:
DELETE FROM UserAuthTable;
DELETE FROM UserAuthTable;
1 change: 1 addition & 0 deletions domain/data/src/commonMain/sqldelight/migrations/1.sqm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE UserAuthTable ADD COLUMN password64 TEXT;
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class UserAuthUseCase(
private suspend fun apiLogin(id: String, password: String): UserAuthTable {
return try {
val userAuth = api.login(id, password)
userAuth.toUserAuthTable()
userAuth.toUserAuthTable(password)
} catch (e: Exception) {
if (e.message == "Connection reset") {
apiLogin(id, password)
Expand All @@ -32,6 +32,14 @@ class UserAuthUseCase(
}
}

suspend fun getUsername(): String {
return userAuthDao.getUsername()
}

suspend fun getPassword(): String? {
return userAuthDao.getBase64EncodedPassword()
}

suspend fun getToken(): String {
return userAuthDao.getToken()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package mehiz.abdallah.progres.domain.models

import mehiz.abdallah.progres.api.dto.UserAuthDto
import mehiz.abdallah.progres.data.db.UserAuthTable
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

Expand All @@ -28,14 +30,16 @@ fun UserAuthTable.toUserAuthModel(): UserAuthModel {
)
}

fun UserAuthDto.toUserAuthTable(): UserAuthTable {
@OptIn(ExperimentalEncodingApi::class)
fun UserAuthDto.toUserAuthTable(password: String): UserAuthTable {
return UserAuthTable(
individualId = individualId,
token = token,
expirationDate = expirationDate,
userId = userId,
uuid = uuid,
establishmentId = establishmentId,
userName = userName
userName = userName,
password64 = Base64.encode(password.encodeToByteArray())
)
}
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ androidx-splashscreen = { module = "androidx.core:core-splashscreen", version =
androidx-credentials-core = { module = "androidx.credentials:credentials", version.ref = "androidx-credentials" }
androidx-credentials-compat = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "androidx-credentials" }
androidx-workmanager = { module = "androidx.work:work-runtime-ktx", version = "2.9.1" }

immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.8" }

kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.1" }
Expand Down Expand Up @@ -103,7 +102,6 @@ connectivity-compose-http = { module = "dev.jordond.connectivity:connectivity-co
sonner = { module = "io.github.dokar3:sonner", version = "0.3.8" }

placeholder = { module = "com.eygraber:compose-placeholder-material3", version = "1.0.8" }
kvault = { module = "com.liftric:kvault", version = "1.12.0" }

qrscan = { module = "io.github.kalinjul.easyqrscan:scanner", version = "0.2.0" }
compottie = { module = "io.github.alexzhirkevich:compottie", version.ref = "compottie" }
Expand Down

0 comments on commit 5fa083f

Please sign in to comment.