Merge pull request #3370 from element-hq/feature/bma/cacheFile
Provide distinct cache directory to the Rust SDK.
This commit is contained in:
commit
3efb371d34
19 changed files with 207 additions and 45 deletions
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.migration.impl.migrations
|
||||||
|
|
||||||
|
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import io.element.android.libraries.di.CacheDirectory
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the cache directory for the existing sessions.
|
||||||
|
*/
|
||||||
|
@ContributesMultibinding(AppScope::class)
|
||||||
|
class AppMigration06 @Inject constructor(
|
||||||
|
private val sessionStore: SessionStore,
|
||||||
|
@CacheDirectory private val cacheDirectory: File,
|
||||||
|
) : AppMigration {
|
||||||
|
override val order: Int = 6
|
||||||
|
|
||||||
|
override suspend fun migrate() {
|
||||||
|
val allSessions = sessionStore.getAllSessions()
|
||||||
|
for (session in allSessions) {
|
||||||
|
if (session.cachePath.isEmpty()) {
|
||||||
|
val sessionFile = File(session.sessionPath)
|
||||||
|
val sessionFolder = sessionFile.name
|
||||||
|
val cachePath = File(cacheDirectory, sessionFolder)
|
||||||
|
sessionStore.updateData(session.copy(cachePath = cachePath.absolutePath))
|
||||||
|
// Move existing cache files
|
||||||
|
listOf(
|
||||||
|
"matrix-sdk-event-cache.sqlite3",
|
||||||
|
"matrix-sdk-event-cache.sqlite3-shm",
|
||||||
|
"matrix-sdk-event-cache.sqlite3-wal",
|
||||||
|
).map { fileName ->
|
||||||
|
File(sessionFile, fileName)
|
||||||
|
}.takeIf { files ->
|
||||||
|
files.all { it.exists() }
|
||||||
|
}?.forEach { cacheFile ->
|
||||||
|
val targetFile = File(cachePath, cacheFile.name)
|
||||||
|
cacheFile.copyTo(targetFile)
|
||||||
|
cacheFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -289,6 +289,7 @@ class DefaultBugReporterTest {
|
||||||
slidingSyncProxy = null,
|
slidingSyncProxy = null,
|
||||||
passphrase = null,
|
passphrase = null,
|
||||||
sessionPath = "session",
|
sessionPath = "session",
|
||||||
|
cachePath = "cache",
|
||||||
)
|
)
|
||||||
@Test
|
@Test
|
||||||
fun `test sendBugReport error`() = runTest {
|
fun `test sendBugReport error`() = runTest {
|
||||||
|
|
|
||||||
|
|
@ -52,5 +52,6 @@ fun aSessionData(
|
||||||
loginType = LoginType.UNKNOWN,
|
loginType = LoginType.UNKNOWN,
|
||||||
passphrase = null,
|
passphrase = null,
|
||||||
sessionPath = "/a/path/to/a/session",
|
sessionPath = "/a/path/to/a/session",
|
||||||
|
cachePath = "/a/path/to/a/cache",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import io.element.android.libraries.matrix.impl.media.RustMediaLoader
|
||||||
import io.element.android.libraries.matrix.impl.notification.RustNotificationService
|
import io.element.android.libraries.matrix.impl.notification.RustNotificationService
|
||||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.impl.oidc.toRustAction
|
import io.element.android.libraries.matrix.impl.oidc.toRustAction
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||||
|
|
@ -67,7 +68,7 @@ import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
|
||||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||||
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
|
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
|
||||||
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
||||||
import io.element.android.libraries.matrix.impl.util.SessionDirectoryProvider
|
import io.element.android.libraries.matrix.impl.util.SessionPathsProvider
|
||||||
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
|
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
|
||||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||||
|
|
@ -161,7 +162,7 @@ class RustMatrixClient(
|
||||||
sessionDispatcher = sessionDispatcher,
|
sessionDispatcher = sessionDispatcher,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val sessionDirectoryProvider = SessionDirectoryProvider(sessionStore)
|
private val sessionPathsProvider = SessionPathsProvider(sessionStore)
|
||||||
|
|
||||||
private val isLoggingOut = AtomicBoolean(false)
|
private val isLoggingOut = AtomicBoolean(false)
|
||||||
|
|
||||||
|
|
@ -186,7 +187,7 @@ class RustMatrixClient(
|
||||||
isTokenValid = false,
|
isTokenValid = false,
|
||||||
loginType = existingData.loginType,
|
loginType = existingData.loginType,
|
||||||
passphrase = existingData.passphrase,
|
passphrase = existingData.passphrase,
|
||||||
sessionPath = existingData.sessionPath,
|
sessionPaths = existingData.getSessionPaths(),
|
||||||
)
|
)
|
||||||
sessionStore.updateData(newData)
|
sessionStore.updateData(newData)
|
||||||
clientLog.d("Removed session data with access token: '$anonymizedAccessToken'.")
|
clientLog.d("Removed session data with access token: '$anonymizedAccessToken'.")
|
||||||
|
|
@ -217,7 +218,7 @@ class RustMatrixClient(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = existingData.loginType,
|
loginType = existingData.loginType,
|
||||||
passphrase = existingData.passphrase,
|
passphrase = existingData.passphrase,
|
||||||
sessionPath = existingData.sessionPath,
|
sessionPaths = existingData.getSessionPaths(),
|
||||||
)
|
)
|
||||||
sessionStore.updateData(newData)
|
sessionStore.updateData(newData)
|
||||||
clientLog.d("Saved new session data with access token: '$anonymizedAccessToken'.")
|
clientLog.d("Saved new session data with access token: '$anonymizedAccessToken'.")
|
||||||
|
|
@ -617,16 +618,17 @@ class RustMatrixClient(
|
||||||
private suspend fun File.getCacheSize(
|
private suspend fun File.getCacheSize(
|
||||||
includeCryptoDb: Boolean = false,
|
includeCryptoDb: Boolean = false,
|
||||||
): Long = withContext(sessionDispatcher) {
|
): Long = withContext(sessionDispatcher) {
|
||||||
val sessionDirectory = sessionDirectoryProvider.provides(sessionId) ?: return@withContext 0L
|
val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L
|
||||||
|
val cacheSize = sessionDirectory.cacheDirectory.getSizeOfFiles()
|
||||||
if (includeCryptoDb) {
|
if (includeCryptoDb) {
|
||||||
sessionDirectory.getSizeOfFiles()
|
cacheSize + sessionDirectory.fileDirectory.getSizeOfFiles()
|
||||||
} else {
|
} else {
|
||||||
listOf(
|
cacheSize + listOf(
|
||||||
"matrix-sdk-state.sqlite3",
|
"matrix-sdk-state.sqlite3",
|
||||||
"matrix-sdk-state.sqlite3-shm",
|
"matrix-sdk-state.sqlite3-shm",
|
||||||
"matrix-sdk-state.sqlite3-wal",
|
"matrix-sdk-state.sqlite3-wal",
|
||||||
).map { fileName ->
|
).map { fileName ->
|
||||||
File(sessionDirectory, fileName)
|
File(sessionDirectory.fileDirectory, fileName)
|
||||||
}.sumOf { file ->
|
}.sumOf { file ->
|
||||||
file.length()
|
file.length()
|
||||||
}
|
}
|
||||||
|
|
@ -636,14 +638,16 @@ class RustMatrixClient(
|
||||||
private suspend fun deleteSessionDirectory(
|
private suspend fun deleteSessionDirectory(
|
||||||
deleteCryptoDb: Boolean = false,
|
deleteCryptoDb: Boolean = false,
|
||||||
): Boolean = withContext(sessionDispatcher) {
|
): Boolean = withContext(sessionDispatcher) {
|
||||||
val sessionDirectory = sessionDirectoryProvider.provides(sessionId) ?: return@withContext false
|
val sessionPaths = sessionPathsProvider.provides(sessionId) ?: return@withContext false
|
||||||
|
// Always delete the cache directory
|
||||||
|
sessionPaths.cacheDirectory.deleteRecursively()
|
||||||
if (deleteCryptoDb) {
|
if (deleteCryptoDb) {
|
||||||
// Delete the folder and all its content
|
// Delete the folder and all its content
|
||||||
sessionDirectory.deleteRecursively()
|
sessionPaths.fileDirectory.deleteRecursively()
|
||||||
} else {
|
} else {
|
||||||
// Delete only the state.db file
|
// Do not delete the crypto database files.
|
||||||
sessionDirectory.listFiles().orEmpty()
|
sessionPaths.fileDirectory.listFiles().orEmpty()
|
||||||
.filter { it.name.contains("matrix-sdk-state") }
|
.filterNot { it.name.contains("matrix-sdk-crypto") }
|
||||||
.forEach { file ->
|
.forEach { file ->
|
||||||
Timber.w("Deleting file ${file.name}...")
|
Timber.w("Deleting file ${file.name}...")
|
||||||
file.safeDelete()
|
file.safeDelete()
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.di.CacheDirectory
|
import io.element.android.libraries.di.CacheDirectory
|
||||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
||||||
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
|
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
|
||||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||||
|
|
@ -52,7 +54,7 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
) {
|
) {
|
||||||
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
||||||
val client = getBaseClientBuilder(
|
val client = getBaseClientBuilder(
|
||||||
sessionPath = sessionData.sessionPath,
|
sessionPaths = sessionData.getSessionPaths(),
|
||||||
passphrase = sessionData.passphrase,
|
passphrase = sessionData.passphrase,
|
||||||
slidingSync = if (appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()) {
|
slidingSync = if (appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()) {
|
||||||
ClientBuilderSlidingSync.Simplified
|
ClientBuilderSlidingSync.Simplified
|
||||||
|
|
@ -87,14 +89,16 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getBaseClientBuilder(
|
internal fun getBaseClientBuilder(
|
||||||
sessionPath: String,
|
sessionPaths: SessionPaths,
|
||||||
passphrase: String?,
|
passphrase: String?,
|
||||||
slidingSyncProxy: String? = null,
|
slidingSyncProxy: String? = null,
|
||||||
slidingSync: ClientBuilderSlidingSync,
|
slidingSync: ClientBuilderSlidingSync,
|
||||||
): ClientBuilder {
|
): ClientBuilder {
|
||||||
return ClientBuilder()
|
return ClientBuilder()
|
||||||
// TODO SDK claims it's valid to use the same path for data and cache, but would be better to use different paths
|
.sessionPaths(
|
||||||
.sessionPaths(dataPath = sessionPath, cachePath = sessionPath)
|
dataPath = sessionPaths.fileDirectory.absolutePath,
|
||||||
|
cachePath = sessionPaths.cacheDirectory.absolutePath,
|
||||||
|
)
|
||||||
.passphrase(passphrase)
|
.passphrase(passphrase)
|
||||||
.slidingSyncProxy(slidingSyncProxy)
|
.slidingSyncProxy(slidingSyncProxy)
|
||||||
.userAgent(userAgentProvider.provide())
|
.userAgent(userAgentProvider.provide())
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
|
||||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||||
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
||||||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
|
@ -53,14 +55,12 @@ import org.matrix.rustcomponents.sdk.QrLoginProgressListener
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk.OidcAuthorizationData
|
import uniffi.matrix_sdk.OidcAuthorizationData
|
||||||
import java.io.File
|
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
class RustMatrixAuthenticationService @Inject constructor(
|
class RustMatrixAuthenticationService @Inject constructor(
|
||||||
private val baseDirectory: File,
|
private val sessionPathsFactory: SessionPathsFactory,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||||
|
|
@ -73,14 +73,14 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
|
|
||||||
// Need to keep a copy of the current session path to eventually delete it.
|
// Need to keep a copy of the current session path to eventually delete it.
|
||||||
// Ideally it would be possible to get the sessionPath from the Client to avoid doing this.
|
// Ideally it would be possible to get the sessionPath from the Client to avoid doing this.
|
||||||
private var sessionPath: File? = null
|
private var sessionPaths: SessionPaths? = null
|
||||||
private var currentClient: Client? = null
|
private var currentClient: Client? = null
|
||||||
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||||
|
|
||||||
private fun rotateSessionPath(): File {
|
private fun rotateSessionPath(): SessionPaths {
|
||||||
sessionPath?.deleteRecursively()
|
sessionPaths?.deleteRecursively()
|
||||||
return File(baseDirectory, UUID.randomUUID().toString())
|
return sessionPathsFactory.create()
|
||||||
.also { sessionPath = it }
|
.also { sessionPaths = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
||||||
|
|
@ -145,14 +145,14 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||||
val currentSessionPath = sessionPath ?: error("You need to call `setHomeserver()` first")
|
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
|
||||||
client.login(username, password, "Element X Android", null)
|
client.login(username, password, "Element X Android", null)
|
||||||
val sessionData = client.session()
|
val sessionData = client.session()
|
||||||
.toSessionData(
|
.toSessionData(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = LoginType.PASSWORD,
|
loginType = LoginType.PASSWORD,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
sessionPath = currentSessionPath.absolutePath,
|
sessionPaths = currentSessionPaths,
|
||||||
)
|
)
|
||||||
clear()
|
clear()
|
||||||
sessionStore.storeData(sessionData)
|
sessionStore.storeData(sessionData)
|
||||||
|
|
@ -196,14 +196,14 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
return withContext(coroutineDispatchers.io) {
|
return withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||||
val currentSessionPath = sessionPath ?: error("You need to call `setHomeserver()` first")
|
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
|
||||||
val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first")
|
val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first")
|
||||||
client.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
client.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
||||||
val sessionData = client.session().toSessionData(
|
val sessionData = client.session().toSessionData(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = LoginType.OIDC,
|
loginType = LoginType.OIDC,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
sessionPath = currentSessionPath.absolutePath,
|
sessionPaths = currentSessionPaths,
|
||||||
)
|
)
|
||||||
clear()
|
clear()
|
||||||
pendingOidcAuthorizationData?.close()
|
pendingOidcAuthorizationData?.close()
|
||||||
|
|
@ -218,10 +218,10 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
|
|
||||||
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
|
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
val emptySessionPath = rotateSessionPath()
|
val emptySessionPaths = rotateSessionPath()
|
||||||
runCatching {
|
runCatching {
|
||||||
val client = rustMatrixClientFactory.getBaseClientBuilder(
|
val client = rustMatrixClientFactory.getBaseClientBuilder(
|
||||||
sessionPath = emptySessionPath.absolutePath,
|
sessionPaths = emptySessionPaths,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL,
|
slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL,
|
||||||
slidingSync = ClientBuilderSlidingSync.Discovered,
|
slidingSync = ClientBuilderSlidingSync.Discovered,
|
||||||
|
|
@ -242,7 +242,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = LoginType.QR,
|
loginType = LoginType.QR,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
sessionPath = emptySessionPath.absolutePath,
|
sessionPaths = emptySessionPaths,
|
||||||
)
|
)
|
||||||
sessionStore.storeData(sessionData)
|
sessionStore.storeData(sessionData)
|
||||||
SessionId(sessionData.userId)
|
SessionId(sessionData.userId)
|
||||||
|
|
@ -262,10 +262,10 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBaseClientBuilder(
|
private fun getBaseClientBuilder(
|
||||||
sessionPath: File,
|
sessionPaths: SessionPaths,
|
||||||
) = rustMatrixClientFactory
|
) = rustMatrixClientFactory
|
||||||
.getBaseClientBuilder(
|
.getBaseClientBuilder(
|
||||||
sessionPath = sessionPath.absolutePath,
|
sessionPaths = sessionPaths,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL,
|
slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL,
|
||||||
slidingSync = ClientBuilderSlidingSync.Discovered,
|
slidingSync = ClientBuilderSlidingSync.Discovered,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.mapper
|
package io.element.android.libraries.matrix.impl.mapper
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
import org.matrix.rustcomponents.sdk.Session
|
import org.matrix.rustcomponents.sdk.Session
|
||||||
|
|
@ -25,7 +26,7 @@ internal fun Session.toSessionData(
|
||||||
isTokenValid: Boolean,
|
isTokenValid: Boolean,
|
||||||
loginType: LoginType,
|
loginType: LoginType,
|
||||||
passphrase: String?,
|
passphrase: String?,
|
||||||
sessionPath: String,
|
sessionPaths: SessionPaths,
|
||||||
homeserverUrl: String? = null,
|
homeserverUrl: String? = null,
|
||||||
) = SessionData(
|
) = SessionData(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
|
|
@ -39,5 +40,6 @@ internal fun Session.toSessionData(
|
||||||
isTokenValid = isTokenValid,
|
isTokenValid = isTokenValid,
|
||||||
loginType = loginType,
|
loginType = loginType,
|
||||||
passphrase = passphrase,
|
passphrase = passphrase,
|
||||||
sessionPath = sessionPath,
|
sessionPath = sessionPaths.fileDirectory.absolutePath,
|
||||||
|
cachePath = sessionPaths.cacheDirectory.absolutePath,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.paths
|
||||||
|
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class SessionPaths(
|
||||||
|
val fileDirectory: File,
|
||||||
|
val cacheDirectory: File,
|
||||||
|
) {
|
||||||
|
fun deleteRecursively() {
|
||||||
|
fileDirectory.deleteRecursively()
|
||||||
|
cacheDirectory.deleteRecursively()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun SessionData.getSessionPaths(): SessionPaths {
|
||||||
|
return SessionPaths(
|
||||||
|
fileDirectory = File(sessionPath),
|
||||||
|
cacheDirectory = File(cachePath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.paths
|
||||||
|
|
||||||
|
import io.element.android.libraries.di.CacheDirectory
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SessionPathsFactory @Inject constructor(
|
||||||
|
private val baseDirectory: File,
|
||||||
|
@CacheDirectory private val cacheDirectory: File,
|
||||||
|
) {
|
||||||
|
fun create(): SessionPaths {
|
||||||
|
val subPath = UUID.randomUUID().toString()
|
||||||
|
return SessionPaths(
|
||||||
|
fileDirectory = File(baseDirectory, subPath),
|
||||||
|
cacheDirectory = File(cacheDirectory, subPath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,15 +17,15 @@
|
||||||
package io.element.android.libraries.matrix.impl.util
|
package io.element.android.libraries.matrix.impl.util
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class SessionDirectoryProvider @Inject constructor(
|
class SessionPathsProvider(
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
) {
|
) {
|
||||||
suspend fun provides(sessionId: SessionId): File? {
|
suspend fun provides(sessionId: SessionId): SessionPaths? {
|
||||||
val path = sessionStore.getSession(sessionId.value)?.sessionPath ?: return null
|
val sessionData = sessionStore.getSession(sessionId.value) ?: return null
|
||||||
return File(path)
|
return sessionData.getSessionPaths()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +44,8 @@ data class SessionData(
|
||||||
val loginType: LoginType,
|
val loginType: LoginType,
|
||||||
/** The optional passphrase used to encrypt data in the SDK local store. */
|
/** The optional passphrase used to encrypt data in the SDK local store. */
|
||||||
val passphrase: String?,
|
val passphrase: String?,
|
||||||
/** The path to the session data stored in the filesystem. */
|
/** The paths to the session data stored in the filesystem. */
|
||||||
val sessionPath: String,
|
val sessionPath: String,
|
||||||
|
/** The path to the cache data stored for the session in the filesystem. */
|
||||||
|
val cachePath: String,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ internal fun SessionData.toDbModel(): DbSessionData {
|
||||||
loginType = loginType.name,
|
loginType = loginType.name,
|
||||||
passphrase = passphrase,
|
passphrase = passphrase,
|
||||||
sessionPath = sessionPath,
|
sessionPath = sessionPath,
|
||||||
|
cachePath = cachePath,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,5 +53,6 @@ internal fun DbSessionData.toApiModel(): SessionData {
|
||||||
loginType = LoginType.fromName(loginType ?: LoginType.UNKNOWN.name),
|
loginType = LoginType.fromName(loginType ?: LoginType.UNKNOWN.name),
|
||||||
passphrase = passphrase,
|
passphrase = passphrase,
|
||||||
sessionPath = sessionPath,
|
sessionPath = sessionPath,
|
||||||
|
cachePath = cachePath,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -25,7 +25,9 @@ CREATE TABLE SessionData (
|
||||||
-- added in version 5
|
-- added in version 5
|
||||||
passphrase TEXT,
|
passphrase TEXT,
|
||||||
-- added in version 6
|
-- added in version 6
|
||||||
sessionPath TEXT NOT NULL DEFAULT ""
|
sessionPath TEXT NOT NULL DEFAULT "",
|
||||||
|
-- added in version 9
|
||||||
|
cachePath TEXT NOT NULL DEFAULT ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
-- Migrate DB from version 8
|
||||||
|
-- Add cachePath so we can track the anonymized path for the session cache dir
|
||||||
|
|
||||||
|
ALTER TABLE SessionData ADD COLUMN cachePath TEXT NOT NULL DEFAULT "";
|
||||||
|
|
@ -145,6 +145,7 @@ class DatabaseSessionStoreTest {
|
||||||
loginType = null,
|
loginType = null,
|
||||||
passphrase = "aPassphrase",
|
passphrase = "aPassphrase",
|
||||||
sessionPath = "sessionPath",
|
sessionPath = "sessionPath",
|
||||||
|
cachePath = "cachePath",
|
||||||
)
|
)
|
||||||
val secondSessionData = SessionData(
|
val secondSessionData = SessionData(
|
||||||
userId = "userId",
|
userId = "userId",
|
||||||
|
|
@ -159,6 +160,7 @@ class DatabaseSessionStoreTest {
|
||||||
loginType = null,
|
loginType = null,
|
||||||
passphrase = "aPassphraseAltered",
|
passphrase = "aPassphraseAltered",
|
||||||
sessionPath = "sessionPath",
|
sessionPath = "sessionPath",
|
||||||
|
cachePath = "cachePath",
|
||||||
)
|
)
|
||||||
assertThat(firstSessionData.userId).isEqualTo(secondSessionData.userId)
|
assertThat(firstSessionData.userId).isEqualTo(secondSessionData.userId)
|
||||||
assertThat(firstSessionData.loginTimestamp).isNotEqualTo(secondSessionData.loginTimestamp)
|
assertThat(firstSessionData.loginTimestamp).isNotEqualTo(secondSessionData.loginTimestamp)
|
||||||
|
|
@ -196,6 +198,7 @@ class DatabaseSessionStoreTest {
|
||||||
loginType = null,
|
loginType = null,
|
||||||
passphrase = "aPassphrase",
|
passphrase = "aPassphrase",
|
||||||
sessionPath = "sessionPath",
|
sessionPath = "sessionPath",
|
||||||
|
cachePath = "cachePath",
|
||||||
)
|
)
|
||||||
val secondSessionData = SessionData(
|
val secondSessionData = SessionData(
|
||||||
userId = "userIdUnknown",
|
userId = "userIdUnknown",
|
||||||
|
|
@ -210,6 +213,7 @@ class DatabaseSessionStoreTest {
|
||||||
loginType = null,
|
loginType = null,
|
||||||
passphrase = "aPassphraseAltered",
|
passphrase = "aPassphraseAltered",
|
||||||
sessionPath = "sessionPath",
|
sessionPath = "sessionPath",
|
||||||
|
cachePath = "cachePath",
|
||||||
)
|
)
|
||||||
assertThat(firstSessionData.userId).isNotEqualTo(secondSessionData.userId)
|
assertThat(firstSessionData.userId).isNotEqualTo(secondSessionData.userId)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,5 @@ internal fun aSessionData() = SessionData(
|
||||||
loginType = LoginType.UNKNOWN.name,
|
loginType = LoginType.UNKNOWN.name,
|
||||||
passphrase = null,
|
passphrase = null,
|
||||||
sessionPath = "sessionPath",
|
sessionPath = "sessionPath",
|
||||||
|
cachePath = "cachePath",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -37,5 +37,6 @@ fun aSessionData(
|
||||||
loginType = LoginType.UNKNOWN,
|
loginType = LoginType.UNKNOWN,
|
||||||
passphrase = null,
|
passphrase = null,
|
||||||
sessionPath = "/a/path/to/a/session",
|
sessionPath = "/a/path/to/a/session",
|
||||||
|
cachePath = "/a/path/to/a/cache",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
||||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||||
import io.element.android.libraries.matrix.impl.auth.OidcConfigurationProvider
|
import io.element.android.libraries.matrix.impl.auth.OidcConfigurationProvider
|
||||||
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
|
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||||
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
||||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||||
|
|
@ -49,7 +50,7 @@ class MainActivity : ComponentActivity() {
|
||||||
val userCertificatesProvider = NoOpUserCertificatesProvider()
|
val userCertificatesProvider = NoOpUserCertificatesProvider()
|
||||||
val proxyProvider = NoOpProxyProvider()
|
val proxyProvider = NoOpProxyProvider()
|
||||||
RustMatrixAuthenticationService(
|
RustMatrixAuthenticationService(
|
||||||
baseDirectory = baseDirectory,
|
sessionPathsFactory = SessionPathsFactory(baseDirectory, applicationContext.cacheDir),
|
||||||
coroutineDispatchers = Singleton.coroutineDispatchers,
|
coroutineDispatchers = Singleton.coroutineDispatchers,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
rustMatrixClientFactory = RustMatrixClientFactory(
|
rustMatrixClientFactory = RustMatrixClientFactory(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue