From 0211197c479e94747d1a2daea7dd8a513273d412 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 2 Feb 2021 22:25:49 +0100 Subject: [PATCH] SSO UIA for add 3pid + refactoring move RegistrationFlow to api --- .../auth/UserInteractiveAuthInterceptor.kt | 2 +- .../registration/RegistrationFlowResponse.kt | 8 +- .../android/sdk/api/failure/Extensions.kt | 4 +- .../matrix/android/sdk/api/failure/Failure.kt | 2 +- .../sdk/api/session/profile/ProfileService.kt | 4 +- .../registration/DefaultRegistrationWizard.kt | 1 + .../sdk/internal/auth/registration/UIAExt.kt | 5 - .../session/profile/DefaultProfileService.kt | 10 +- .../profile/FinalizeAddThreePidBody.kt | 3 +- .../profile/FinalizeAddingThreePidTask.kt | 30 ++--- .../vector/app/core/error/ErrorFormatter.kt | 4 +- .../app/core/error/SsoFlowNotSupportedYet.kt | 19 ---- .../app/core/platform/VectorBaseFragment.kt | 1 + .../app/features/auth/ReAuthActivity.kt | 4 +- .../recover/BootstrapSharedViewModel.kt | 4 +- .../crypto/recover/BootstrapViewEvents.kt | 2 +- .../features/home/HomeActivityViewModel.kt | 4 +- .../DeactivateAccountViewEvents.kt | 2 +- .../DeactivateAccountViewModel.kt | 2 +- .../CrossSigningSettingsViewEvents.kt | 2 +- .../CrossSigningSettingsViewModel.kt | 4 +- .../settings/devices/DevicesViewEvents.kt | 2 +- .../settings/devices/DevicesViewModel.kt | 4 +- .../threepids/ThreePidsSettingsAction.kt | 7 +- .../threepids/ThreePidsSettingsFragment.kt | 43 +++++-- .../threepids/ThreePidsSettingsViewEvents.kt | 4 +- .../threepids/ThreePidsSettingsViewModel.kt | 107 +++++++++++------- vector/src/main/res/values/strings.xml | 1 + 28 files changed, 164 insertions(+), 121 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/auth/registration/RegistrationFlowResponse.kt (93%) delete mode 100644 vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt index e7f27f458c..df509191b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UserInteractiveAuthInterceptor.kt @@ -16,7 +16,7 @@ package org.matrix.android.sdk.api.auth -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth import kotlin.coroutines.Continuation diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt index 3461a4d738..e74f2568e3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/RegistrationFlowResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.auth.registration +package org.matrix.android.sdk.api.auth.registration import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -109,3 +109,9 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult { return FlowResult(missingStage, completedStage) } + + +fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? { + val completed = completedStages ?: emptyList() + return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt index a0983161af..c06cdd9e23 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.api.failure +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.di.MoshiProvider import java.io.IOException import javax.net.ssl.HttpsURLConnection @@ -59,7 +59,7 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? { .adapter(RegistrationFlowResponse::class.java) .fromJson(this.errorBody) } - } else if (this is Failure.ServerError && this.error.code == MatrixError.M_FORBIDDEN) { + } else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) { // This happens when the submission for this stage was bad (like bad password) if (this.error.session != null && this.error.flows != null) { RegistrationFlowResponse( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt index de881b9e02..b241903364 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Failure.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.api.failure +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.crypto.MXCryptoError -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.network.ssl.Fingerprint import java.io.IOException diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt index 1fd8360253..a4d5b665c6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/profile/ProfileService.kt @@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.profile import android.net.Uri import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.JsonDict @@ -107,8 +108,7 @@ interface ProfileService { * Finalize adding a 3Pids. Call this method once the user has validated that he owns the ThreePid */ fun finalizeAddingThreePid(threePid: ThreePid, - uiaSession: String?, - accountPassword: String?, + userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, matrixCallback: MatrixCallback): Cancelable /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index 9c6b942a4f..163009d918 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.auth.registration.RegistrationResult import org.matrix.android.sdk.api.auth.registration.RegistrationWizard +import org.matrix.android.sdk.api.auth.registration.toFlowResult import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError import org.matrix.android.sdk.api.util.Cancelable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt index 23273178e5..8c9a6c2514 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/UIAExt.kt @@ -23,11 +23,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth import timber.log.Timber import kotlin.coroutines.suspendCoroutine -fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? { - val completed = completedStages ?: emptyList() - return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() } -} - suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean { Timber.d("## UIA: check error ${failure.message}") val flowResponse = failure.toRegistrationFlowResponse() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt index 500d43408e..b3216d744d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import io.realm.kotlin.where import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.util.Cancelable @@ -170,14 +171,12 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto } override fun finalizeAddingThreePid(threePid: ThreePid, - uiaSession: String?, - accountPassword: String?, + userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor, matrixCallback: MatrixCallback): Cancelable { return finalizeAddingThreePidTask .configureWith(FinalizeAddingThreePidTask.Params( threePid = threePid, - session = uiaSession, - accountPassword = accountPassword, + userInteractiveAuthInterceptor = userInteractiveAuthInterceptor, userWantsToCancel = false )) { callback = alsoRefresh(matrixCallback) @@ -189,8 +188,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto return finalizeAddingThreePidTask .configureWith(FinalizeAddingThreePidTask.Params( threePid = threePid, - session = null, - accountPassword = null, + userInteractiveAuthInterceptor = null, userWantsToCancel = true )) { callback = alsoRefresh(matrixCallback) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt index 4e46dd096d..6301929545 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddThreePidBody.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.profile import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth @JsonClass(generateAdapter = true) internal data class FinalizeAddThreePidBody( @@ -37,5 +36,5 @@ internal data class FinalizeAddThreePidBody( * Additional authentication information for the user-interactive authentication API. */ @Json(name = "auth") - val auth: UserPasswordAuth? + val auth: Map? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt index 1e3a2cb501..96d878c205 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt @@ -17,10 +17,12 @@ package org.matrix.android.sdk.internal.session.profile import com.zhuinden.monarchy.Monarchy +import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth +import org.matrix.android.sdk.internal.auth.registration.handleUIA +import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase @@ -29,13 +31,14 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction +import timber.log.Timber import javax.inject.Inject internal abstract class FinalizeAddingThreePidTask : Task { data class Params( val threePid: ThreePid, - val session: String?, - val accountPassword: String?, + val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor?, + val userAuthParam: UIABaseAuth? = null, val userWantsToCancel: Boolean ) } @@ -62,20 +65,21 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor( val body = FinalizeAddThreePidBody( clientSecret = pendingThreePids.clientSecret, sid = pendingThreePids.sid, - auth = if (params.session != null && params.accountPassword != null) { - UserPasswordAuth( - session = params.session, - user = userId, - password = params.accountPassword - ) - } else null + auth = params.userAuthParam?.asMap() ) apiCall = profileAPI.finalizeAddThreePid(body) } } catch (throwable: Throwable) { - throw throwable.toRegistrationFlowResponse() - ?.let { Failure.RegistrationFlowError(it) } - ?: throwable + if (params.userInteractiveAuthInterceptor == null + || !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth -> + execute(params.copy(userAuthParam = auth)) + } + ) { + Timber.d("## UIA: propagate failure") + throw throwable.toRegistrationFlowResponse() + ?.let { Failure.RegistrationFlowError(it) } + ?: throwable + } } } diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt index b9bc935890..8258370fe0 100644 --- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt @@ -105,11 +105,13 @@ class DefaultErrorFormatter @Inject constructor( HttpURLConnection.HTTP_NOT_FOUND -> // homeserver not found stringProvider.getString(R.string.login_error_no_homeserver_found) + HttpURLConnection.HTTP_UNAUTHORIZED -> + // uia errors? + stringProvider.getString(R.string.error_unauthorized) else -> throwable.localizedMessage } } - is SsoFlowNotSupportedYet -> stringProvider.getString(R.string.error_sso_flow_not_supported_yet) else -> throwable.localizedMessage } ?: stringProvider.getString(R.string.unknown_error) diff --git a/vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt b/vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt deleted file mode 100644 index 7b22072c34..0000000000 --- a/vector/src/main/java/im/vector/app/core/error/SsoFlowNotSupportedYet.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 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.app.core.error - -class SsoFlowNotSupportedYet : Throwable() diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 6f41a6a846..0b951fb5a2 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -200,6 +200,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScre } protected fun showLoadingDialog(message: CharSequence? = null, cancelable: Boolean = false) { + progress?.dismiss() progress = ProgressDialog(requireContext()).apply { setCancelable(cancelable) setMessage(message ?: getString(R.string.please_wait)) diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt index b44639750e..220b48c1cb 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt @@ -37,8 +37,8 @@ import im.vector.app.core.utils.openUrlInChromeCustomTab import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.LoginFlowTypes -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 9282dc3e3e..4a6be1e4e9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -43,8 +43,8 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt index f4ec9d68e4..3f06623ad6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt @@ -17,7 +17,7 @@ package im.vector.app.features.crypto.recover import im.vector.app.core.platform.VectorViewEvents -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse sealed class BootstrapViewEvents : VectorViewEvents { data class Dismiss(val success: Boolean) : BootstrapViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index f8e61b5e04..fb68cc4706 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -40,8 +40,8 @@ import org.matrix.android.sdk.api.session.InitialSyncProgressService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt index 05200c3aa3..1b0ec2de0c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewEvents.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.account.deactivation import im.vector.app.core.platform.VectorViewEvents -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse /** * Transient events for deactivate account settings screen diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index dc5415a6bb..81ae4721fe 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.failure.isInvalidUIAAuth import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt index 89970b130a..1c11560d40 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewEvents.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.crosssigning import im.vector.app.core.platform.VectorViewEvents -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse /** * Transient events for cross signing settings screen diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index ceb216ca42..38fd256494 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -37,8 +37,8 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt index fd50fe6f16..8535c698a7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.devices import im.vector.app.core.platform.VectorViewEvents import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 610c89706e..01f7b7f176 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -49,8 +49,8 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt index 0be3c6a198..d223009e69 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsAction.kt @@ -25,6 +25,11 @@ sealed class ThreePidsSettingsAction : VectorViewModelAction { data class SubmitCode(val threePid: ThreePid.Msisdn, val code: String) : ThreePidsSettingsAction() data class ContinueThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() data class CancelThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() - data class AccountPassword(val password: String) : ThreePidsSettingsAction() + + // data class AccountPassword(val password: String) : ThreePidsSettingsAction() data class DeleteThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() + + object SsoAuthDone : ThreePidsSettingsAction() + data class PasswordAuthDone(val password: String) : ThreePidsSettingsAction() + object ReAuthCancelled : ThreePidsSettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt index d6da04affc..0a7489e2cc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.threepids +import android.app.Activity import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater @@ -26,7 +27,6 @@ import androidx.appcompat.app.AppCompatActivity import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R -import im.vector.app.core.dialogs.PromptPasswordDialog import im.vector.app.core.dialogs.withColoredButton import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -35,10 +35,12 @@ import im.vector.app.core.extensions.getFormattedValue import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.isMsisdn +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - +import im.vector.app.features.auth.ReAuthActivity +import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.identity.ThreePid import javax.inject.Inject @@ -64,15 +66,42 @@ class ThreePidsSettingsFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) - ThreePidsSettingsViewEvents.RequestPassword -> askUserPassword() + is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) + is ThreePidsSettingsViewEvents.RequestReAuth -> askAuthentication(it) }.exhaustive } } - private fun askUserPassword() { - PromptPasswordDialog().show(requireActivity()) { password -> - viewModel.handle(ThreePidsSettingsAction.AccountPassword(password)) + // private fun askUserPassword() { +// PromptPasswordDialog().show(requireActivity()) { password -> +// viewModel.handle(ThreePidsSettingsAction.AccountPassword(password)) +// } +// } + + private fun askAuthentication(event: ThreePidsSettingsViewEvents.RequestReAuth) { + ReAuthActivity.newIntent(requireContext(), + event.registrationFlowResponse, + event.lastErrorCode, + getString(R.string.settings_add_email_address)).let { intent -> + reAuthActivityResultLauncher.launch(intent) + } + } + private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) { + LoginFlowTypes.SSO -> { + viewModel.handle(ThreePidsSettingsAction.SsoAuthDone) + } + LoginFlowTypes.PASSWORD -> { + val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: "" + viewModel.handle(ThreePidsSettingsAction.PasswordAuthDone(password)) + } + else -> { + viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled) + } + } + } else { + viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt index 1ac2d10458..0346fd137e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt @@ -17,8 +17,10 @@ package im.vector.app.features.settings.threepids import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse sealed class ThreePidsSettingsViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : ThreePidsSettingsViewEvents() - object RequestPassword : ThreePidsSettingsViewEvents() +// object RequestPassword : ThreePidsSettingsViewEvents() + data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : ThreePidsSettingsViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index 65cfadb6a9..9c025ab220 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -24,21 +24,28 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R -import im.vector.app.core.error.SsoFlowNotSupportedYet import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ReadOnceTrue +import im.vector.app.features.auth.ReAuthActivity import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.auth.data.LoginFlowTypes -import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 +import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth +import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth +import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth import org.matrix.android.sdk.rx.rx +import timber.log.Timber +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume class ThreePidsSettingsViewModel @AssistedInject constructor( @Assisted initialState: ThreePidsSettingsViewState, @@ -48,36 +55,16 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( // UIA session private var pendingThreePid: ThreePid? = null - private var pendingSession: String? = null +// private var pendingSession: String? = null private val loadingCallback: MatrixCallback = object : MatrixCallback { override fun onFailure(failure: Throwable) { isLoading(false) - - if (failure is Failure.RegistrationFlowError) { - var isPasswordRequestFound = false - - // We only support LoginFlowTypes.PASSWORD - // Check if we can provide the user password - failure.registrationFlowResponse.flows?.forEach { interactiveAuthenticationFlow -> - isPasswordRequestFound = isPasswordRequestFound || interactiveAuthenticationFlow.stages?.any { it == LoginFlowTypes.PASSWORD } == true - } - - if (isPasswordRequestFound) { - pendingSession = failure.registrationFlowResponse.session - _viewEvents.post(ThreePidsSettingsViewEvents.RequestPassword) - } else { - // LoginFlowTypes.PASSWORD not supported, and this is the only one Element supports so far... - _viewEvents.post(ThreePidsSettingsViewEvents.Failure(SsoFlowNotSupportedYet())) - } - } else { - _viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure)) - } + _viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure)) } override fun onSuccess(data: Unit) { pendingThreePid = null - pendingSession = null isLoading(false) } } @@ -142,16 +129,50 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( override fun handle(action: ThreePidsSettingsAction) { when (action) { - is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) + is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action) - is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) - is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) - is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action) - is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) - is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) + is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) + is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) + is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) + is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) + ThreePidsSettingsAction.SsoAuthDone -> { + Timber.d("## UIA - FallBack success") + if (pendingAuth != null) { + uiaContinuation?.resume(pendingAuth!!) + } else { + uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException()))) + } + } + is ThreePidsSettingsAction.PasswordAuthDone -> { + val decryptedPass = session.loadSecureSecret(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?.resumeWith(Result.failure((Exception()))) + uiaContinuation = null + pendingAuth = null + } }.exhaustive } + var uiaContinuation: Continuation? = null + var pendingAuth: UIABaseAuth? = null + + private val uiaInterceptor = object : UserInteractiveAuthInterceptor { + override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { + _viewEvents.post(ThreePidsSettingsViewEvents.RequestReAuth(flowResponse, errCode)) + pendingAuth = DefaultBaseAuth(session = flowResponse.session) + uiaContinuation = promise + } + } + private fun handleSubmitCode(action: ThreePidsSettingsAction.SubmitCode) { isLoading(true) setState { @@ -168,7 +189,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( override fun onSuccess(data: Unit) { // then finalize pendingThreePid = action.threePid - session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback) + session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback) } override fun onFailure(failure: Throwable) { @@ -232,7 +253,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( isLoading(true) pendingThreePid = action.threePid viewModelScope.launch { - session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback) + session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback) } } @@ -243,16 +264,14 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } } - private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) { - val safeSession = pendingSession ?: return Unit - .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending session"))) } - val safeThreePid = pendingThreePid ?: return Unit - .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) } - isLoading(true) - viewModelScope.launch { - session.finalizeAddingThreePid(safeThreePid, safeSession, action.password, loadingCallback) - } - } +// private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) { +// val safeThreePid = pendingThreePid ?: return Unit +// .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) } +// isLoading(true) +// viewModelScope.launch { +// session.finalizeAddingThreePid(safeThreePid, uiaInterceptor, loadingCallback) +// } +// } private fun handleDeleteThreePid(action: ThreePidsSettingsAction.DeleteThreePid) { isLoading(true) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 5f9f801887..68f1f16d6d 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -333,6 +333,7 @@ Your device is using an outdated TLS security protocol, vulnerable to attack, for your security you will not be able to connect Invalid username/password + Unauthorized, missing valid authentication credentials The access token specified was not recognised Malformed JSON Did not contain valid JSON