SoftLogout: Store the info that the token is not valid anymore for a faster startup

This commit is contained in:
Benoit Marty 2019-12-12 15:16:37 +01:00
parent 261b4be287
commit a5af949c15
10 changed files with 77 additions and 10 deletions

View file

@ -22,5 +22,6 @@ package im.vector.matrix.android.api.auth.data
*/
data class SessionParams(
val credentials: Credentials,
val homeServerConnectionConfig: HomeServerConnectionConfig
val homeServerConnectionConfig: HomeServerConnectionConfig,
val isTokenValid: Boolean
)

View file

@ -62,6 +62,11 @@ interface Session :
*/
val sessionParams: SessionParams
/**
* The session is valid, i.e. it has a valid token so far
*/
val isOpenable: Boolean
/**
* Useful shortcut to get access to the userId
*/

View file

@ -60,7 +60,8 @@ internal class DefaultSessionCreator @Inject constructor(
?.also { Timber.d("Overriding identity server url to $it") }
?.let { Uri.parse(it) }
?: homeServerConnectionConfig.identityServerUri
))
),
isTokenValid = true)
sessionParamsStore.save(sessionParams)
return sessionManager.getOrCreateSession(sessionParams)

View file

@ -29,6 +29,8 @@ internal interface SessionParamsStore {
suspend fun save(sessionParams: SessionParams)
suspend fun setTokenInvalid(userId: String)
suspend fun updateCredentials(newCredentials: Credentials)
suspend fun delete(userId: String)

View file

@ -24,7 +24,7 @@ internal class AuthRealmMigration : RealmMigration {
companion object {
// Current schema version
const val SCHEMA_VERSION = 1L
const val SCHEMA_VERSION = 2L
}
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@ -46,5 +46,14 @@ internal class AuthRealmMigration : RealmMigration {
.addField(PendingSessionEntityFields.IS_REGISTRATION_STARTED, Boolean::class.java)
.addField(PendingSessionEntityFields.CURRENT_THREE_PID_DATA_JSON, String::class.java)
}
if (oldVersion <= 1) {
Timber.d("Step 1 -> 2")
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) }
}
}
}

View file

@ -76,6 +76,26 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
}
}
override suspend fun setTokenInvalid(userId: String) {
awaitTransaction(realmConfiguration) { realm ->
val currentSessionParams = realm
.where(SessionParamsEntity::class.java)
.equalTo(SessionParamsEntityFields.USER_ID, userId)
.findAll()
.firstOrNull()
if (currentSessionParams == null) {
// Should not happen
"Session param not found for user $userId"
.let { Timber.w(it) }
.also { error(it) }
} else {
currentSessionParams.isTokenValid = false
}
}
}
override suspend fun updateCredentials(newCredentials: Credentials) {
awaitTransaction(realmConfiguration) { realm ->
val currentSessionParams = realm
@ -92,7 +112,8 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
.also { error(it) }
} else {
val newSessionParams = currentSessionParams.copy(
credentials = newCredentials
credentials = newCredentials,
isTokenValid = true
)
val entity = mapper.map(newSessionParams)

View file

@ -22,5 +22,8 @@ import io.realm.annotations.PrimaryKey
internal open class SessionParamsEntity(
@PrimaryKey var userId: 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
// In case of hard logout, this object in deleted from DB
var isTokenValid: Boolean = true
) : RealmObject()

View file

@ -36,7 +36,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
if (credentials == null || homeServerConnectionConfig == null) {
return null
}
return SessionParams(credentials, homeServerConnectionConfig)
return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid)
}
fun map(sessionParams: SessionParams?): SessionParamsEntity? {
@ -48,6 +48,10 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) {
if (credentialsJson == null || homeServerConnectionConfigJson == null) {
return null
}
return SessionParamsEntity(sessionParams.credentials.userId, credentialsJson, homeServerConnectionConfigJson)
return SessionParamsEntity(
sessionParams.credentials.userId,
credentialsJson,
homeServerConnectionConfigJson,
sessionParams.isTokenValid)
}
}

View file

@ -42,10 +42,14 @@ import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.session.sync.job.SyncThread
import im.vector.matrix.android.internal.session.sync.job.SyncWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -72,6 +76,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private val secureStorageService: Lazy<SecureStorageService>,
private val syncThreadProvider: Provider<SyncThread>,
private val contentUrlResolver: ContentUrlResolver,
private val sessionParamsStore: SessionParamsStore,
private val contentUploadProgressTracker: ContentUploadStateTracker,
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>)
@ -94,6 +99,9 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private var syncThread: SyncThread? = null
override val isOpenable: Boolean
get() = sessionParamsStore.get(myUserId)?.isTokenValid ?: false
@MainThread
override fun open() {
assertMainThread()
@ -171,6 +179,14 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
@Subscribe(threadMode = ThreadMode.MAIN)
fun onGlobalError(globalError: GlobalError) {
if (globalError is GlobalError.InvalidToken
&& globalError.softLogout) {
// Mark the token has invalid
GlobalScope.launch(Dispatchers.IO) {
sessionParamsStore.setTokenInvalid(myUserId)
}
}
sessionListeners.dispatchGlobalError(globalError)
}

View file

@ -169,9 +169,14 @@ class MainActivity : VectorBaseActivity() {
// the homeserver has invalidated the token (password changed, device deleted, other security reason
SignedOutActivity.newIntent(this)
sessionHolder.hasActiveSession() ->
// We have a session. In case of soft logout (i.e. restart of the app after a soft logout)
// the app will try to sync and will reenter the soft logout use case
// We have a session.
// Check it can be opened
if(sessionHolder.getActiveSession().isOpenable) {
HomeActivity.newIntent(this)
} else {
// The token is still invalid
SoftLogoutActivity.newIntent(this)
}
else ->
// First start, or no active session
LoginActivity.newIntent(this, null)