Mutualize pending auth handling

This commit is contained in:
Maxime NATUREL 2022-09-21 14:55:37 +02:00
parent 6c79aae3aa
commit 892fd4445c
6 changed files with 106 additions and 179 deletions

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2022 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.app.features.auth
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.util.fromBase64
import timber.log.Timber
import javax.inject.Inject
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
class PendingAuthHandler @Inject constructor(
private val matrix: Matrix,
private val activeSessionHolder: ActiveSessionHolder,
) {
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
fun ssoAuthDone() {
Timber.d("ssoAuthDone $pendingAuth , continuation: $uiaContinuation")
pendingAuth?.let {
uiaContinuation?.resume(it)
} ?: run {
uiaContinuation?.resumeWithException(IllegalArgumentException())
}
}
fun passwordAuthDone(password: String) {
Timber.d("passwordAuthDone")
val decryptedPass = matrix.secureStorageService()
.loadSecureSecret<String>(
inputStream = password.fromBase64().inputStream(),
keyAlias = ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = activeSessionHolder.getActiveSession().myUserId
)
)
}
fun reAuthCancelled() {
Timber.d("reAuthCancelled")
uiaContinuation?.resumeWithException(Exception())
uiaContinuation = null
pendingAuth = null
}
}

View file

@ -32,14 +32,13 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.PendingAuthHandler
import im.vector.app.features.raw.wellknown.SecureBackupMethod import im.vector.app.features.raw.wellknown.SecureBackupMethod
import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isSecureBackupRequired import im.vector.app.features.raw.wellknown.isSecureBackupRequired
import im.vector.app.features.raw.wellknown.secureBackupMethod import im.vector.app.features.raw.wellknown.secureBackupMethod
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.UserPasswordAuth
@ -57,10 +56,8 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.api.util.awaitCallback import org.matrix.android.sdk.api.util.awaitCallback
import org.matrix.android.sdk.api.util.fromBase64
import java.io.OutputStream import java.io.OutputStream
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
class BootstrapSharedViewModel @AssistedInject constructor( class BootstrapSharedViewModel @AssistedInject constructor(
@ -71,7 +68,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private val rawService: RawService, private val rawService: RawService,
private val bootstrapTask: BootstrapCrossSigningTask, private val bootstrapTask: BootstrapCrossSigningTask,
private val migrationTask: BackupToQuadSMigrationTask, private val migrationTask: BackupToQuadSMigrationTask,
private val matrix: Matrix, private val pendingAuthHandler: PendingAuthHandler,
) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) { ) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) {
private var doesKeyBackupExist: Boolean = false private var doesKeyBackupExist: Boolean = false
@ -85,11 +82,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> by hiltMavericksViewModelFactory()
// private var _pendingSession: String? = null
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
init { init {
setState { setState {
@ -272,21 +264,10 @@ class BootstrapSharedViewModel @AssistedInject constructor(
is BootstrapActions.DoMigrateWithRecoveryKey -> { is BootstrapActions.DoMigrateWithRecoveryKey -> {
startMigrationFlow(state.step, null, action.recoveryKey) startMigrationFlow(state.step, null, action.recoveryKey)
} }
BootstrapActions.SsoAuthDone -> { BootstrapActions.SsoAuthDone -> pendingAuthHandler.ssoAuthDone()
uiaContinuation?.resume(DefaultBaseAuth(session = pendingAuth?.session ?: "")) is BootstrapActions.PasswordAuthDone -> pendingAuthHandler.passwordAuthDone(action.password)
}
is BootstrapActions.PasswordAuthDone -> {
val decryptedPass = matrix.secureStorageService()
.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
}
BootstrapActions.ReAuthCancelled -> { BootstrapActions.ReAuthCancelled -> {
pendingAuthHandler.reAuthCancelled()
setState { setState {
copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error))) copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error)))
} }
@ -402,13 +383,13 @@ class BootstrapSharedViewModel @AssistedInject constructor(
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
when (flowResponse.nextUncompletedStage()) { when (flowResponse.nextUncompletedStage()) {
LoginFlowTypes.PASSWORD -> { LoginFlowTypes.PASSWORD -> {
pendingAuth = UserPasswordAuth( pendingAuthHandler.pendingAuth = UserPasswordAuth(
// Note that _pendingSession may or may not be null, this is OK, it will be managed by the task // Note that _pendingSession may or may not be null, this is OK, it will be managed by the task
session = flowResponse.session, session = flowResponse.session,
user = session.myUserId, user = session.myUserId,
password = null password = null
) )
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
setState { setState {
copy( copy(
step = BootstrapStep.AccountReAuth() step = BootstrapStep.AccountReAuth()
@ -417,8 +398,8 @@ class BootstrapSharedViewModel @AssistedInject constructor(
_viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode)) _viewEvents.post(BootstrapViewEvents.RequestReAuth(flowResponse, errCode))
} }
LoginFlowTypes.SSO -> { LoginFlowTypes.SSO -> {
pendingAuth = DefaultBaseAuth(flowResponse.session) pendingAuthHandler.pendingAuth = DefaultBaseAuth(flowResponse.session)
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
setState { setState {
copy( copy(
step = BootstrapStep.AccountReAuth() step = BootstrapStep.AccountReAuth()

View file

@ -23,22 +23,15 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.PendingAuthHandler
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.failure.isInvalidUIAAuth import org.matrix.android.sdk.api.failure.isInvalidUIAAuth
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.api.session.uia.exceptions.UiaCancelledException
import org.matrix.android.sdk.api.util.fromBase64
import timber.log.Timber
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
data class DeactivateAccountViewState( data class DeactivateAccountViewState(
val dummy: Boolean = false val dummy: Boolean = false
@ -47,7 +40,7 @@ data class DeactivateAccountViewState(
class DeactivateAccountViewModel @AssistedInject constructor( class DeactivateAccountViewModel @AssistedInject constructor(
@Assisted private val initialState: DeactivateAccountViewState, @Assisted private val initialState: DeactivateAccountViewState,
private val session: Session, private val session: Session,
private val matrix: Matrix, private val pendingAuthHandler: PendingAuthHandler,
) : ) :
VectorViewModel<DeactivateAccountViewState, DeactivateAccountAction, DeactivateAccountViewEvents>(initialState) { VectorViewModel<DeactivateAccountViewState, DeactivateAccountAction, DeactivateAccountViewEvents>(initialState) {
@ -56,39 +49,15 @@ class DeactivateAccountViewModel @AssistedInject constructor(
override fun create(initialState: DeactivateAccountViewState): DeactivateAccountViewModel override fun create(initialState: DeactivateAccountViewState): DeactivateAccountViewModel
} }
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
override fun handle(action: DeactivateAccountAction) { override fun handle(action: DeactivateAccountAction) {
when (action) { when (action) {
is DeactivateAccountAction.DeactivateAccount -> handleDeactivateAccount(action) is DeactivateAccountAction.DeactivateAccount -> handleDeactivateAccount(action)
DeactivateAccountAction.SsoAuthDone -> { DeactivateAccountAction.SsoAuthDone -> pendingAuthHandler.ssoAuthDone()
Timber.d("## UIA - FallBack success")
_viewEvents.post(DeactivateAccountViewEvents.Loading())
if (pendingAuth != null) {
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWithException(IllegalArgumentException())
}
}
is DeactivateAccountAction.PasswordAuthDone -> { is DeactivateAccountAction.PasswordAuthDone -> {
_viewEvents.post(DeactivateAccountViewEvents.Loading()) _viewEvents.post(DeactivateAccountViewEvents.Loading())
val decryptedPass = matrix.secureStorageService() pendingAuthHandler.passwordAuthDone(action.password)
.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
}
DeactivateAccountAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
uiaContinuation?.resumeWithException(UiaCancelledException())
uiaContinuation = null
pendingAuth = null
} }
DeactivateAccountAction.ReAuthCancelled -> pendingAuthHandler.reAuthCancelled()
} }
} }
@ -102,8 +71,8 @@ class DeactivateAccountViewModel @AssistedInject constructor(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
_viewEvents.post(DeactivateAccountViewEvents.RequestReAuth(flowResponse, errCode)) _viewEvents.post(DeactivateAccountViewEvents.RequestReAuth(flowResponse, errCode))
pendingAuth = DefaultBaseAuth(session = flowResponse.session) pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = flowResponse.session)
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
} }
} }
) )

View file

@ -24,12 +24,11 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.PendingAuthHandler
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.UserPasswordAuth
@ -40,19 +39,17 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.api.util.awaitCallback import org.matrix.android.sdk.api.util.awaitCallback
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
class CrossSigningSettingsViewModel @AssistedInject constructor( class CrossSigningSettingsViewModel @AssistedInject constructor(
@Assisted private val initialState: CrossSigningSettingsViewState, @Assisted private val initialState: CrossSigningSettingsViewState,
private val session: Session, private val session: Session,
private val reAuthHelper: ReAuthHelper, private val reAuthHelper: ReAuthHelper,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val matrix: Matrix, private val pendingAuthHandler: PendingAuthHandler,
) : VectorViewModel<CrossSigningSettingsViewState, CrossSigningSettingsAction, CrossSigningSettingsViewEvents>(initialState) { ) : VectorViewModel<CrossSigningSettingsViewState, CrossSigningSettingsAction, CrossSigningSettingsViewEvents>(initialState) {
init { init {
@ -77,9 +74,6 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
} }
} }
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
@AssistedFactory @AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<CrossSigningSettingsViewModel, CrossSigningSettingsViewState> { interface Factory : MavericksAssistedViewModelFactory<CrossSigningSettingsViewModel, CrossSigningSettingsViewState> {
override fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel override fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel
@ -110,8 +104,8 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
} else { } else {
Timber.d("## UIA : initializeCrossSigning UIA > start reauth activity") Timber.d("## UIA : initializeCrossSigning UIA > start reauth activity")
_viewEvents.post(CrossSigningSettingsViewEvents.RequestReAuth(flowResponse, errCode)) _viewEvents.post(CrossSigningSettingsViewEvents.RequestReAuth(flowResponse, errCode))
pendingAuth = DefaultBaseAuth(session = flowResponse.session) pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = flowResponse.session)
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
} }
} }
}, it }, it
@ -125,31 +119,11 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
} }
Unit Unit
} }
is CrossSigningSettingsAction.SsoAuthDone -> { is CrossSigningSettingsAction.SsoAuthDone -> pendingAuthHandler.ssoAuthDone()
Timber.d("## UIA - FallBack success") is CrossSigningSettingsAction.PasswordAuthDone -> pendingAuthHandler.passwordAuthDone(action.password)
if (pendingAuth != null) {
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWithException(IllegalArgumentException())
}
}
is CrossSigningSettingsAction.PasswordAuthDone -> {
val decryptedPass = matrix.secureStorageService()
.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
}
CrossSigningSettingsAction.ReAuthCancelled -> { CrossSigningSettingsAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
_viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView) _viewEvents.post(CrossSigningSettingsViewEvents.HideModalWaitingView)
uiaContinuation?.resumeWithException(Exception()) pendingAuthHandler.reAuthCancelled()
uiaContinuation = null
pendingAuth = null
} }
} }
} }

View file

@ -32,7 +32,7 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.PublishDataSource import im.vector.app.core.utils.PublishDataSource
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.PendingAuthHandler
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
@ -45,7 +45,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
@ -67,13 +66,11 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.api.util.awaitCallback import org.matrix.android.sdk.api.util.awaitCallback
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import timber.log.Timber import timber.log.Timber
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
data class DevicesViewState( data class DevicesViewState(
val myDeviceId: String = "", val myDeviceId: String = "",
@ -100,15 +97,12 @@ class DevicesViewModel @AssistedInject constructor(
private val session: Session, private val session: Session,
private val reAuthHelper: ReAuthHelper, private val reAuthHelper: ReAuthHelper,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val matrix: Matrix, private val pendingAuthHandler: PendingAuthHandler,
private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase,
getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase,
) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState), VerificationService.Listener { ) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState), VerificationService.Listener {
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
@AssistedFactory @AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<DevicesViewModel, DevicesViewState> { interface Factory : MavericksAssistedViewModelFactory<DevicesViewModel, DevicesViewState> {
override fun create(initialState: DevicesViewState): DevicesViewModel override fun create(initialState: DevicesViewState): DevicesViewModel
@ -232,37 +226,9 @@ class DevicesViewModel @AssistedInject constructor(
is DevicesAction.CompleteSecurity -> handleCompleteSecurity() is DevicesAction.CompleteSecurity -> handleCompleteSecurity()
is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action) is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action)
is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action) is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action)
is DevicesAction.SsoAuthDone -> { is DevicesAction.SsoAuthDone -> pendingAuthHandler.ssoAuthDone()
// we should use token based auth is DevicesAction.PasswordAuthDone -> pendingAuthHandler.passwordAuthDone(action.password)
// _viewEvents.post(CrossSigningSettingsViewEvents.ShowModalWaitingView(null)) DevicesAction.ReAuthCancelled -> pendingAuthHandler.reAuthCancelled()
// will release the interactive auth interceptor
Timber.d("## UIA - FallBack success $pendingAuth , continuation: $uiaContinuation")
if (pendingAuth != null) {
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWithException(IllegalArgumentException())
}
Unit
}
is DevicesAction.PasswordAuthDone -> {
val decryptedPass = matrix.secureStorageService()
.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
Unit
}
DevicesAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
// _viewEvents.post(DevicesViewEvents.Loading)
uiaContinuation?.resumeWithException(Exception())
uiaContinuation = null
pendingAuth = null
}
DevicesAction.ResetSecurity -> _viewEvents.post(DevicesViewEvents.PromptResetSecrets) DevicesAction.ResetSecurity -> _viewEvents.post(DevicesViewEvents.PromptResetSecrets)
} }
} }
@ -371,8 +337,8 @@ class DevicesViewModel @AssistedInject constructor(
} else { } else {
Timber.d("## UIA : deleteDevice UIA > start reauth activity") Timber.d("## UIA : deleteDevice UIA > start reauth activity")
_viewEvents.post(DevicesViewEvents.RequestReAuth(flowResponse, errCode)) _viewEvents.post(DevicesViewEvents.RequestReAuth(flowResponse, errCode))
pendingAuth = DefaultBaseAuth(session = flowResponse.session) pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = flowResponse.session)
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
} }
} }
}, it) }, it)

View file

@ -28,33 +28,26 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ReadOnceTrue import im.vector.app.core.utils.ReadOnceTrue
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.PendingAuthHandler
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import timber.log.Timber
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
class ThreePidsSettingsViewModel @AssistedInject constructor( class ThreePidsSettingsViewModel @AssistedInject constructor(
@Assisted initialState: ThreePidsSettingsViewState, @Assisted initialState: ThreePidsSettingsViewState,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val matrix: Matrix, private val pendingAuthHandler: PendingAuthHandler,
) : VectorViewModel<ThreePidsSettingsViewState, ThreePidsSettingsAction, ThreePidsSettingsViewEvents>(initialState) { ) : VectorViewModel<ThreePidsSettingsViewState, ThreePidsSettingsAction, ThreePidsSettingsViewEvents>(initialState) {
// UIA session // UIA session
private var pendingThreePid: ThreePid? = null private var pendingThreePid: ThreePid? = null
// private var pendingSession: String? = null
private suspend fun loadingSuspendable(block: suspend () -> Unit) { private suspend fun loadingSuspendable(block: suspend () -> Unit) {
runCatching { block() } runCatching { block() }
@ -126,42 +119,17 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action)
ThreePidsSettingsAction.SsoAuthDone -> { ThreePidsSettingsAction.SsoAuthDone -> pendingAuthHandler.ssoAuthDone()
Timber.d("## UIA - FallBack success") is ThreePidsSettingsAction.PasswordAuthDone -> pendingAuthHandler.passwordAuthDone(action.password)
if (pendingAuth != null) { ThreePidsSettingsAction.ReAuthCancelled -> pendingAuthHandler.reAuthCancelled()
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWithException(IllegalArgumentException())
} }
} }
is ThreePidsSettingsAction.PasswordAuthDone -> {
val decryptedPass = matrix.secureStorageService()
.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
}
ThreePidsSettingsAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
uiaContinuation?.resumeWithException(Exception())
uiaContinuation = null
pendingAuth = null
}
}
}
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
private val uiaInterceptor = object : UserInteractiveAuthInterceptor { private val uiaInterceptor = object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
_viewEvents.post(ThreePidsSettingsViewEvents.RequestReAuth(flowResponse, errCode)) _viewEvents.post(ThreePidsSettingsViewEvents.RequestReAuth(flowResponse, errCode))
pendingAuth = DefaultBaseAuth(session = flowResponse.session) pendingAuthHandler.pendingAuth = DefaultBaseAuth(session = flowResponse.session)
uiaContinuation = promise pendingAuthHandler.uiaContinuation = promise
} }
} }