From 1de85daad9765dbd2ecd97ecc5ee901312ddfcc4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 13 Dec 2019 11:58:02 +0100 Subject: [PATCH] SoftLogout: handle the case where user sign in with SSO on another account --- .../features/login/AbstractLoginFragment.kt | 2 + .../riotx/features/login/LoginActivity.kt | 2 + .../signout/soft/SoftLogoutActivity.kt | 39 +++++++++++++ .../signout/soft/SoftLogoutViewEvents.kt | 26 +++++++++ .../signout/soft/SoftLogoutViewModel.kt | 57 ++++++++++++------- vector/src/main/res/values/strings_riotX.xml | 1 + 6 files changed, 107 insertions(+), 20 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt diff --git a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt index cf8a0fb6f3..c2d0dcd43e 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/AbstractLoginFragment.kt @@ -29,6 +29,7 @@ import im.vector.matrix.android.api.failure.MatrixError import im.vector.riotx.R import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.VectorBaseFragment +import io.reactivex.android.schedulers.AndroidSchedulers import javax.net.ssl.HttpsURLConnection /** @@ -60,6 +61,7 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed { loginViewModel.viewEvents .observe() + .observeOn(AndroidSchedulers.mainThread()) .subscribe { handleLoginViewEvents(it) } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt index 2efce4d8f7..d879212c3d 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt @@ -44,6 +44,7 @@ import im.vector.riotx.features.home.HomeActivity import im.vector.riotx.features.login.terms.LoginTermsFragment import im.vector.riotx.features.login.terms.LoginTermsFragmentArgument import im.vector.riotx.features.login.terms.toLocalizedLoginTerms +import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_login.* import javax.inject.Inject @@ -112,6 +113,7 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { loginViewModel.viewEvents .observe() + .observeOn(AndroidSchedulers.mainThread()) .subscribe { handleLoginViewEvents(it) } diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt index c173a43e5a..454d894e96 100644 --- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutActivity.kt @@ -18,17 +18,21 @@ package im.vector.riotx.features.signout.soft import android.content.Context import android.content.Intent +import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible +import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import im.vector.matrix.android.api.failure.GlobalError import im.vector.matrix.android.api.session.Session import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.replaceFragment import im.vector.riotx.features.MainActivity import im.vector.riotx.features.MainActivityArgs import im.vector.riotx.features.login.LoginActivity +import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_login.* import timber.log.Timber import javax.inject.Inject @@ -43,6 +47,7 @@ class SoftLogoutActivity : LoginActivity() { @Inject lateinit var softLogoutViewModelFactory: SoftLogoutViewModel.Factory @Inject lateinit var session: Session + @Inject lateinit var errorFormatter: ErrorFormatter override fun injectWith(injector: ScreenComponent) { super.injectWith(injector) @@ -56,6 +61,40 @@ class SoftLogoutActivity : LoginActivity() { .subscribe(this) { updateWithState(it) } + + softLogoutViewModel.viewEvents + .observe() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + handleSoftLogoutViewEvents(it) + } + .disposeOnDestroy() + } + + private fun handleSoftLogoutViewEvents(softLogoutViewEvents: SoftLogoutViewEvents) { + when (softLogoutViewEvents) { + is SoftLogoutViewEvents.Error -> + showError(errorFormatter.toHumanReadable(softLogoutViewEvents.throwable)) + is SoftLogoutViewEvents.ErrorNotSameUser -> { + // Pop the backstack + supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + + // And inform the user + showError(getString( + R.string.soft_logout_sso_not_same_user_error, + softLogoutViewEvents.currentUserId, + softLogoutViewEvents.newUserId) + ) + } + } + } + + private fun showError(message: String) { + AlertDialog.Builder(this) + .setTitle(R.string.dialog_title_error) + .setMessage(message) + .setPositiveButton(R.string.ok, null) + .show() } override fun addFirstFragment() { diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt new file mode 100644 index 0000000000..ce92cd67e6 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewEvents.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2019 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.riotx.features.signout.soft + +/** + * Transient events for SoftLogout + */ +sealed class SoftLogoutViewEvents { + data class ErrorNotSameUser(val currentUserId: String, val newUserId: String) : SoftLogoutViewEvents() + data class Error(val throwable: Throwable) : SoftLogoutViewEvents() +} diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt index f3d908c7a4..8e4b053e9c 100644 --- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutViewModel.kt @@ -28,7 +28,10 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.extensions.hasUnsavedKeys import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.utils.DataSource +import im.vector.riotx.core.utils.PublishDataSource import im.vector.riotx.features.login.LoginMode +import timber.log.Timber /** * TODO Test push: disable the pushers? @@ -68,6 +71,9 @@ class SoftLogoutViewModel @AssistedInject constructor( private var currentTask: Cancelable? = null + private val _viewEvents = PublishDataSource() + val viewEvents: DataSource = _viewEvents + init { // Get the supported login flow getSupportedLoginFlow() @@ -88,7 +94,6 @@ class SoftLogoutViewModel @AssistedInject constructor( currentTask = authenticationService.getLoginFlow(homeServerConnectionConfig, object : MatrixCallback { override fun onFailure(failure: Throwable) { - // TODO _viewEvents.post(LoginViewEvents.Error(failure)) setState { copy( asyncHomeServerLoginFlowRequest = Fail(failure) @@ -167,26 +172,38 @@ class SoftLogoutViewModel @AssistedInject constructor( } private fun handleWebLoginSuccess(action: SoftLogoutAction.WebLoginSuccess) { - setState { - copy( - asyncLoginAction = Loading() - ) - } - currentTask = session.updateCredentials(action.credentials, - object : MatrixCallback { - override fun onFailure(failure: Throwable) { - setState { - copy( - asyncLoginAction = Fail(failure) - ) - } - } - - override fun onSuccess(data: Unit) { - onSessionRestored() - } + // User may have been connected with SSO with another userId + // We have to check this + withState { softLogoutViewState -> + if (softLogoutViewState.userId != action.credentials.userId) { + Timber.w("User login again with SSO, but using another account") + _viewEvents.post(SoftLogoutViewEvents.ErrorNotSameUser( + softLogoutViewState.userId, + action.credentials.userId)) + } else { + setState { + copy( + asyncLoginAction = Loading() + ) } - ) + currentTask = session.updateCredentials(action.credentials, + object : MatrixCallback { + override fun onFailure(failure: Throwable) { + _viewEvents.post(SoftLogoutViewEvents.Error(failure)) + setState { + copy( + asyncLoginAction = Uninitialized + ) + } + } + + override fun onSuccess(data: Unit) { + onSessionRestored() + } + } + ) + } + } } private fun handleSignInAgain(action: SoftLogoutAction.SignInAgain) { diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 51ff817578..8c37ffd4b3 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -159,5 +159,6 @@ Clear all data currently stored on this device?\nSign in again to access your account data and messages. You’ll lose access to secure messages unless you sign in to recover your encryption keys. Clear data + The current session is for user %1$s and you provide credentials for user %2$s. This is not supported by RiotX.\nPlease first clear data, then sign in again on another account.