mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 17:35:54 +03:00
Merge branch 'develop' into feature/stabilization
This commit is contained in:
commit
42c7421b05
11 changed files with 119 additions and 41 deletions
|
@ -14,7 +14,7 @@ Improvements 🙌:
|
||||||
- Fix autocompletion issues and add support for rooms, groups, and emoji (#780)
|
- Fix autocompletion issues and add support for rooms, groups, and emoji (#780)
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
- Change the way RiotX identifies a session to allow the SDK to support several sessions with the same user (#800)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Fix crash when opening room creation screen from the room filtering screen
|
- Fix crash when opening room creation screen from the room filtering screen
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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 im.vector.matrix.android.internal.auth
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.util.md5
|
||||||
|
|
||||||
|
internal fun createSessionId(userId: String, deviceId: String?): String {
|
||||||
|
return (if (deviceId.isNullOrBlank()) userId else "$userId|$deviceId").md5()
|
||||||
|
}
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package im.vector.matrix.android.internal.auth.db
|
package im.vector.matrix.android.internal.auth.db
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.internal.auth.createSessionId
|
||||||
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import io.realm.DynamicRealm
|
import io.realm.DynamicRealm
|
||||||
import io.realm.RealmMigration
|
import io.realm.RealmMigration
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -23,35 +26,60 @@ import timber.log.Timber
|
||||||
internal object AuthRealmMigration : RealmMigration {
|
internal object AuthRealmMigration : RealmMigration {
|
||||||
|
|
||||||
// Current schema version
|
// Current schema version
|
||||||
const val SCHEMA_VERSION = 2L
|
const val SCHEMA_VERSION = 3L
|
||||||
|
|
||||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||||
Timber.d("Migrating Auth Realm from $oldVersion to $newVersion")
|
Timber.d("Migrating Auth Realm from $oldVersion to $newVersion")
|
||||||
|
|
||||||
if (oldVersion <= 0) {
|
if (oldVersion <= 0) migrateTo1(realm)
|
||||||
Timber.d("Step 0 -> 1")
|
if (oldVersion <= 1) migrateTo2(realm)
|
||||||
Timber.d("Create PendingSessionEntity")
|
if (oldVersion <= 2) migrateTo3(realm)
|
||||||
|
}
|
||||||
|
|
||||||
realm.schema.create("PendingSessionEntity")
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
.addField(PendingSessionEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON, String::class.java)
|
Timber.d("Step 0 -> 1")
|
||||||
.setRequired(PendingSessionEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON, true)
|
Timber.d("Create PendingSessionEntity")
|
||||||
.addField(PendingSessionEntityFields.CLIENT_SECRET, String::class.java)
|
|
||||||
.setRequired(PendingSessionEntityFields.CLIENT_SECRET, true)
|
|
||||||
.addField(PendingSessionEntityFields.SEND_ATTEMPT, Integer::class.java)
|
|
||||||
.setRequired(PendingSessionEntityFields.SEND_ATTEMPT, true)
|
|
||||||
.addField(PendingSessionEntityFields.RESET_PASSWORD_DATA_JSON, String::class.java)
|
|
||||||
.addField(PendingSessionEntityFields.CURRENT_SESSION, String::class.java)
|
|
||||||
.addField(PendingSessionEntityFields.IS_REGISTRATION_STARTED, Boolean::class.java)
|
|
||||||
.addField(PendingSessionEntityFields.CURRENT_THREE_PID_DATA_JSON, String::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion <= 1) {
|
realm.schema.create("PendingSessionEntity")
|
||||||
Timber.d("Step 1 -> 2")
|
.addField(PendingSessionEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON, String::class.java)
|
||||||
Timber.d("Add boolean isTokenValid in SessionParamsEntity, with value true")
|
.setRequired(PendingSessionEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON, true)
|
||||||
|
.addField(PendingSessionEntityFields.CLIENT_SECRET, String::class.java)
|
||||||
|
.setRequired(PendingSessionEntityFields.CLIENT_SECRET, true)
|
||||||
|
.addField(PendingSessionEntityFields.SEND_ATTEMPT, Integer::class.java)
|
||||||
|
.setRequired(PendingSessionEntityFields.SEND_ATTEMPT, true)
|
||||||
|
.addField(PendingSessionEntityFields.RESET_PASSWORD_DATA_JSON, String::class.java)
|
||||||
|
.addField(PendingSessionEntityFields.CURRENT_SESSION, String::class.java)
|
||||||
|
.addField(PendingSessionEntityFields.IS_REGISTRATION_STARTED, Boolean::class.java)
|
||||||
|
.addField(PendingSessionEntityFields.CURRENT_THREE_PID_DATA_JSON, String::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
realm.schema.get("SessionParamsEntity")
|
private fun migrateTo2(realm: DynamicRealm) {
|
||||||
?.addField(SessionParamsEntityFields.IS_TOKEN_VALID, Boolean::class.java)
|
Timber.d("Step 1 -> 2")
|
||||||
?.transform { it.set(SessionParamsEntityFields.IS_TOKEN_VALID, true) }
|
Timber.d("Add boolean isTokenValid in SessionParamsEntity, with value true")
|
||||||
}
|
|
||||||
|
realm.schema.get("SessionParamsEntity")
|
||||||
|
?.addField(SessionParamsEntityFields.IS_TOKEN_VALID, Boolean::class.java)
|
||||||
|
?.transform { it.set(SessionParamsEntityFields.IS_TOKEN_VALID, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateTo3(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 2 -> 3")
|
||||||
|
Timber.d("Update SessionParamsEntity primary key, to allow several sessions with the same userId")
|
||||||
|
|
||||||
|
realm.schema.get("SessionParamsEntity")
|
||||||
|
?.removePrimaryKey()
|
||||||
|
?.addField(SessionParamsEntityFields.SESSION_ID, String::class.java)
|
||||||
|
?.setRequired(SessionParamsEntityFields.SESSION_ID, true)
|
||||||
|
?.transform {
|
||||||
|
val userId = it.getString(SessionParamsEntityFields.USER_ID)
|
||||||
|
val credentialsJson = it.getString(SessionParamsEntityFields.CREDENTIALS_JSON)
|
||||||
|
|
||||||
|
val credentials = MoshiProvider.providesMoshi()
|
||||||
|
.adapter(Credentials::class.java)
|
||||||
|
.fromJson(credentialsJson)
|
||||||
|
|
||||||
|
it.set(SessionParamsEntityFields.SESSION_ID, createSessionId(userId, credentials?.deviceId))
|
||||||
|
}
|
||||||
|
?.addPrimaryKey(SessionParamsEntityFields.SESSION_ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
internal open class SessionParamsEntity(
|
internal open class SessionParamsEntity(
|
||||||
@PrimaryKey var userId: String = "",
|
@PrimaryKey var sessionId: String = "",
|
||||||
|
var userId: String = "",
|
||||||
var credentialsJson: String = "",
|
var credentialsJson: String = "",
|
||||||
var homeServerConnectionConfigJson: String = "",
|
var homeServerConnectionConfigJson: String = "",
|
||||||
// Set to false when the token is invalid and the user has been soft logged out
|
// Set to false when the token is invalid and the user has been soft logged out
|
||||||
|
|
|
@ -20,6 +20,7 @@ import com.squareup.moshi.Moshi
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.internal.auth.createSessionId
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
||||||
|
@ -49,6 +50,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return SessionParamsEntity(
|
return SessionParamsEntity(
|
||||||
|
createSessionId(sessionParams.credentials.userId, sessionParams.credentials.deviceId),
|
||||||
sessionParams.credentials.userId,
|
sessionParams.credentials.userId,
|
||||||
credentialsJson,
|
credentialsJson,
|
||||||
homeServerConnectionConfigJson,
|
homeServerConnectionConfigJson,
|
||||||
|
|
|
@ -47,7 +47,7 @@ internal abstract class CryptoModule {
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
companion object {
|
companion object {
|
||||||
internal const val DB_ALIAS_PREFIX = "crypto_module_"
|
internal fun getKeyAlias(userMd5: String) = "crypto_module_$userMd5"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -59,7 +59,7 @@ internal abstract class CryptoModule {
|
||||||
return RealmConfiguration.Builder()
|
return RealmConfiguration.Builder()
|
||||||
.directory(directory)
|
.directory(directory)
|
||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, "$DB_ALIAS_PREFIX$userMd5")
|
realmKeysUtils.configureEncryption(this, getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
.name("crypto_store.realm")
|
.name("crypto_store.realm")
|
||||||
.modules(RealmCryptoStoreModule())
|
.modules(RealmCryptoStoreModule())
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
import im.vector.matrix.android.internal.database.model.SessionRealmModule
|
||||||
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.di.UserCacheDirectory
|
import im.vector.matrix.android.internal.di.UserCacheDirectory
|
||||||
import im.vector.matrix.android.internal.di.UserMd5
|
import im.vector.matrix.android.internal.di.UserMd5
|
||||||
import im.vector.matrix.android.internal.session.SessionModule
|
import im.vector.matrix.android.internal.session.SessionModule
|
||||||
|
@ -37,13 +38,14 @@ private const val REALM_NAME = "disk_store.realm"
|
||||||
*/
|
*/
|
||||||
internal class SessionRealmConfigurationFactory @Inject constructor(private val realmKeysUtils: RealmKeysUtils,
|
internal class SessionRealmConfigurationFactory @Inject constructor(private val realmKeysUtils: RealmKeysUtils,
|
||||||
@UserCacheDirectory val directory: File,
|
@UserCacheDirectory val directory: File,
|
||||||
|
@SessionId val sessionId: String,
|
||||||
@UserMd5 val userMd5: String,
|
@UserMd5 val userMd5: String,
|
||||||
context: Context) {
|
context: Context) {
|
||||||
|
|
||||||
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.realm", Context.MODE_PRIVATE)
|
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.realm", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
fun create(): RealmConfiguration {
|
fun create(): RealmConfiguration {
|
||||||
val shouldClearRealm = sharedPreferences.getBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", false)
|
val shouldClearRealm = sharedPreferences.getBoolean("$REALM_SHOULD_CLEAR_FLAG_$sessionId", false)
|
||||||
if (shouldClearRealm) {
|
if (shouldClearRealm) {
|
||||||
Timber.v("************************************************************")
|
Timber.v("************************************************************")
|
||||||
Timber.v("The realm file session was corrupted and couldn't be loaded.")
|
Timber.v("The realm file session was corrupted and couldn't be loaded.")
|
||||||
|
@ -53,7 +55,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(private val
|
||||||
}
|
}
|
||||||
sharedPreferences
|
sharedPreferences
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", true)
|
.putBoolean("$REALM_SHOULD_CLEAR_FLAG_$sessionId", true)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
val realmConfiguration = RealmConfiguration.Builder()
|
val realmConfiguration = RealmConfiguration.Builder()
|
||||||
|
@ -61,7 +63,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(private val
|
||||||
.directory(directory)
|
.directory(directory)
|
||||||
.name(REALM_NAME)
|
.name(REALM_NAME)
|
||||||
.apply {
|
.apply {
|
||||||
realmKeysUtils.configureEncryption(this, "${SessionModule.DB_ALIAS_PREFIX}$userMd5")
|
realmKeysUtils.configureEncryption(this, SessionModule.getKeyAlias(userMd5))
|
||||||
}
|
}
|
||||||
.modules(SessionRealmModule())
|
.modules(SessionRealmModule())
|
||||||
.deleteRealmIfMigrationNeeded()
|
.deleteRealmIfMigrationNeeded()
|
||||||
|
@ -72,7 +74,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(private val
|
||||||
Timber.v("Successfully create realm instance")
|
Timber.v("Successfully create realm instance")
|
||||||
sharedPreferences
|
sharedPreferences
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", false)
|
.putBoolean("$REALM_SHOULD_CLEAR_FLAG_$sessionId", false)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
return realmConfiguration
|
return realmConfiguration
|
||||||
|
|
|
@ -31,3 +31,10 @@ internal annotation class UserId
|
||||||
@Qualifier
|
@Qualifier
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class UserMd5
|
internal annotation class UserMd5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to inject the sessionId, which is defined as md5(userId|deviceId)
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class SessionId
|
||||||
|
|
|
@ -25,7 +25,7 @@ import im.vector.matrix.android.api.session.file.FileService
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
||||||
import im.vector.matrix.android.internal.di.UserMd5
|
import im.vector.matrix.android.internal.di.SessionId
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import im.vector.matrix.android.internal.util.md5
|
import im.vector.matrix.android.internal.util.md5
|
||||||
|
@ -42,7 +42,7 @@ import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultFileService @Inject constructor(private val context: Context,
|
internal class DefaultFileService @Inject constructor(private val context: Context,
|
||||||
@UserMd5 private val userMd5: String,
|
@SessionId private val sessionId: String,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) : FileService {
|
private val coroutineDispatchers: MatrixCoroutineDispatchers) : FileService {
|
||||||
|
|
||||||
|
@ -103,9 +103,9 @@ internal class DefaultFileService @Inject constructor(private val context: Conte
|
||||||
return when (downloadMode) {
|
return when (downloadMode) {
|
||||||
FileService.DownloadMode.FOR_INTERNAL_USE -> {
|
FileService.DownloadMode.FOR_INTERNAL_USE -> {
|
||||||
// Create dir tree (MF stands for Matrix File):
|
// Create dir tree (MF stands for Matrix File):
|
||||||
// <cache>/MF/<md5(userId)>/<md5(id)>/
|
// <cache>/MF/<sessionId>/<md5(id)>/
|
||||||
val tmpFolderRoot = File(context.cacheDir, "MF")
|
val tmpFolderRoot = File(context.cacheDir, "MF")
|
||||||
val tmpFolderUser = File(tmpFolderRoot, userMd5)
|
val tmpFolderUser = File(tmpFolderRoot, sessionId)
|
||||||
File(tmpFolderUser, id.md5())
|
File(tmpFolderUser, id.md5())
|
||||||
}
|
}
|
||||||
FileService.DownloadMode.TO_EXPORT -> {
|
FileService.DownloadMode.TO_EXPORT -> {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
|
||||||
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
|
||||||
|
import im.vector.matrix.android.internal.auth.createSessionId
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
|
import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory
|
||||||
import im.vector.matrix.android.internal.di.*
|
import im.vector.matrix.android.internal.di.*
|
||||||
|
@ -54,8 +55,7 @@ internal abstract class SessionModule {
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
companion object {
|
companion object {
|
||||||
|
internal fun getKeyAlias(userMd5: String) = "session_db_$userMd5"
|
||||||
internal const val DB_ALIAS_PREFIX = "session_db_"
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -83,11 +83,26 @@ internal abstract class SessionModule {
|
||||||
return userId.md5()
|
return userId.md5()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@SessionId
|
||||||
|
@Provides
|
||||||
|
fun providesSessionId(credentials: Credentials): String {
|
||||||
|
return createSessionId(credentials.userId, credentials.deviceId)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@UserCacheDirectory
|
@UserCacheDirectory
|
||||||
fun providesFilesDir(@UserMd5 userMd5: String, context: Context): File {
|
fun providesFilesDir(@UserMd5 userMd5: String,
|
||||||
return File(context.filesDir, userMd5)
|
@SessionId sessionId: String,
|
||||||
|
context: Context): File {
|
||||||
|
// Temporary code for migration
|
||||||
|
val old = File(context.filesDir, userMd5)
|
||||||
|
if (old.exists()) {
|
||||||
|
old.renameTo(File(context.filesDir, sessionId))
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(context.filesDir, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
|
@ -97,8 +97,8 @@ internal class DefaultSignOutTask @Inject constructor(private val context: Conte
|
||||||
userFile.deleteRecursively()
|
userFile.deleteRecursively()
|
||||||
|
|
||||||
Timber.d("SignOut: clear the database keys")
|
Timber.d("SignOut: clear the database keys")
|
||||||
realmKeysUtils.clear(SessionModule.DB_ALIAS_PREFIX + userMd5)
|
realmKeysUtils.clear(SessionModule.getKeyAlias(userMd5))
|
||||||
realmKeysUtils.clear(CryptoModule.DB_ALIAS_PREFIX + userMd5)
|
realmKeysUtils.clear(CryptoModule.getKeyAlias(userMd5))
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
|
|
Loading…
Reference in a new issue