Merge pull request #1281 from vector-im/feature/various_issues_verification_ssss_bootstrap

Feature/various issues verification ssss bootstrap
This commit is contained in:
Benoit Marty 2020-04-27 14:32:18 +02:00 committed by GitHub
commit 4e3df99e42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 122 additions and 216 deletions

View File

@ -16,6 +16,9 @@
package im.vector.matrix.android.api.failure package im.vector.matrix.android.api.failure
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.di.MoshiProvider
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
fun Throwable.is401() = fun Throwable.is401() =
@ -37,3 +40,18 @@ fun Throwable.isInvalidPassword(): Boolean {
&& error.code == MatrixError.M_FORBIDDEN && error.code == MatrixError.M_FORBIDDEN
&& error.message == "Invalid password" && error.message == "Invalid password"
} }
/**
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
*/
fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
return if (this is Failure.OtherServerError && this.httpCode == 401) {
tryThis {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(this.errorBody)
}
} else {
null
}
}

View File

@ -18,8 +18,8 @@ package im.vector.matrix.android.internal.auth.registration
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.internal.auth.AuthAPI import im.vector.matrix.android.internal.auth.AuthAPI
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
@ -39,25 +39,9 @@ internal class DefaultRegisterTask(
apiCall = authAPI.register(params.registrationParams) apiCall = authAPI.register(params.registrationParams)
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) { throw throwable.toRegistrationFlowResponse()
// Parse to get a RegistrationFlowResponse ?.let { Failure.RegistrationFlowError(it) }
val registrationFlowResponse = try { ?: throwable
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(throwable.errorBody)
} catch (e: Exception) {
null
}
// check if the server response can be cast
if (registrationFlowResponse != null) {
throw Failure.RegistrationFlowError(registrationFlowResponse)
} else {
throw throwable
}
} else {
// Other error
throw throwable
}
} }
} }
} }

View File

@ -17,10 +17,9 @@
package im.vector.matrix.android.internal.crypto.tasks package im.vector.matrix.android.internal.crypto.tasks
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.api.CryptoApi
import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -43,25 +42,9 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams())
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) { throw throwable.toRegistrationFlowResponse()
// Parse to get a RegistrationFlowResponse ?.let { Failure.RegistrationFlowError(it) }
val registrationFlowResponse = try { ?: throwable
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(throwable.errorBody)
} catch (e: Exception) {
null
}
// check if the server response can be casted
if (registrationFlowResponse != null) {
throw Failure.RegistrationFlowError(registrationFlowResponse)
} else {
throw throwable
}
} else {
// Other error
throw throwable
}
} }
} }
} }

View File

@ -17,14 +17,13 @@
package im.vector.matrix.android.internal.crypto.tasks package im.vector.matrix.android.internal.crypto.tasks
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.api.CryptoApi
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
import im.vector.matrix.android.internal.crypto.model.rest.UploadSigningKeysBody import im.vector.matrix.android.internal.crypto.model.rest.UploadSigningKeysBody
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.crypto.model.toRest import im.vector.matrix.android.internal.crypto.model.toRest
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -65,37 +64,25 @@ internal class DefaultUploadSigningKeysTask @Inject constructor(
} }
return return
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError val registrationFlowResponse = throwable.toRegistrationFlowResponse()
&& throwable.httpCode == 401 if (registrationFlowResponse != null
&& params.userPasswordAuth != null && params.userPasswordAuth != null
/* Avoid infinite loop */ /* Avoid infinite loop */
&& params.userPasswordAuth.session.isNullOrEmpty() && params.userPasswordAuth.session.isNullOrEmpty()
) { ) {
try { // Retry with authentication
MoshiProvider.providesMoshi() val req = executeRequest<KeysQueryResponse>(eventBus) {
.adapter(RegistrationFlowResponse::class.java) apiCall = cryptoApi.uploadSigningKeys(
.fromJson(throwable.errorBody) uploadQuery.copy(auth = params.userPasswordAuth.copy(session = registrationFlowResponse.session))
} catch (e: Exception) { )
null
}?.let {
// Retry with authentication
try {
val req = executeRequest<KeysQueryResponse>(eventBus) {
apiCall = cryptoApi.uploadSigningKeys(
uploadQuery.copy(auth = params.userPasswordAuth.copy(session = it.session))
)
}
if (req.failures?.isNotEmpty() == true) {
throw UploadSigningKeys(req.failures)
}
return
} catch (failure: Throwable) {
throw failure
}
} }
if (req.failures?.isNotEmpty() == true) {
throw UploadSigningKeys(req.failures)
}
} else {
// Other error
throw throwable
} }
// Other error
throw throwable
} }
} }
} }

View File

@ -16,9 +16,7 @@
package im.vector.matrix.android.internal.session.account package im.vector.matrix.android.internal.session.account
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
@ -45,31 +43,20 @@ internal class DefaultChangePasswordTask @Inject constructor(
apiCall = accountAPI.changePassword(changePasswordParams) apiCall = accountAPI.changePassword(changePasswordParams)
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError val registrationFlowResponse = throwable.toRegistrationFlowResponse()
&& throwable.httpCode == 401
if (registrationFlowResponse != null
/* Avoid infinite loop */ /* Avoid infinite loop */
&& changePasswordParams.auth?.session == null) { && changePasswordParams.auth?.session == null) {
try { // Retry with authentication
MoshiProvider.providesMoshi() executeRequest<Unit>(eventBus) {
.adapter(RegistrationFlowResponse::class.java) apiCall = accountAPI.changePassword(
.fromJson(throwable.errorBody) changePasswordParams.copy(auth = changePasswordParams.auth?.copy(session = registrationFlowResponse.session))
} catch (e: Exception) { )
null
}?.let {
// Retry with authentication
try {
executeRequest<Unit>(eventBus) {
apiCall = accountAPI.changePassword(
changePasswordParams.copy(auth = changePasswordParams.auth?.copy(session = it.session))
)
}
return
} catch (failure: Throwable) {
throw failure
}
} }
} else {
throw throwable
} }
throw throwable
} }
} }
} }

View File

@ -95,6 +95,14 @@ class BootstrapConfirmPassphraseFragment @Inject constructor(
sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility)
} }
.disposeOnDestroyView() .disposeOnDestroyView()
bootstrapSubmit.clicks()
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
submit()
}
.disposeOnDestroyView()
} }
private fun submit() = withState(sharedViewModel) { state -> private fun submit() = withState(sharedViewModel) { state ->
@ -113,8 +121,6 @@ class BootstrapConfirmPassphraseFragment @Inject constructor(
} }
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
super.invalidate()
if (state.step is BootstrapStep.ConfirmPassphrase) { if (state.step is BootstrapStep.ConfirmPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false) ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)

View File

@ -18,6 +18,7 @@ package im.vector.riotx.features.crypto.recover
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
@ -28,13 +29,11 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
import im.vector.matrix.android.api.session.securestorage.SsssKeySpec import im.vector.matrix.android.api.session.securestorage.SsssKeySpec
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.util.awaitCallback import im.vector.matrix.android.internal.util.awaitCallback
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.ViewModelTask import im.vector.riotx.core.platform.ViewModelTask
@ -230,15 +229,10 @@ class BootstrapCrossSigningTask @Inject constructor(
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult { private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) { if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
return BootstrapResult.InvalidPasswordError(failure.error) return BootstrapResult.InvalidPasswordError(failure.error)
} else if (failure is Failure.OtherServerError && failure.httpCode == 401) { } else {
try { val registrationFlowResponse = failure.toRegistrationFlowResponse()
MoshiProvider.providesMoshi() if (registrationFlowResponse != null) {
.adapter(RegistrationFlowResponse::class.java) if (registrationFlowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } != true) {
.fromJson(failure.errorBody)
} catch (e: Exception) {
null
}?.let { flowResponse ->
if (flowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } != true) {
// can't do this from here // can't do this from here
return BootstrapResult.UnsupportedAuthFlow() return BootstrapResult.UnsupportedAuthFlow()
} }

View File

@ -90,6 +90,14 @@ class BootstrapEnterPassphraseFragment @Inject constructor(
sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility)
} }
.disposeOnDestroyView() .disposeOnDestroyView()
bootstrapSubmit.clicks()
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
submit()
}
.disposeOnDestroyView()
} }
private fun submit() = withState(sharedViewModel) { state -> private fun submit() = withState(sharedViewModel) { state ->
@ -108,8 +116,6 @@ class BootstrapEnterPassphraseFragment @Inject constructor(
} }
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
super.invalidate()
if (state.step is BootstrapStep.SetupPassphrase) { if (state.step is BootstrapStep.SetupPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false) ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)

View File

@ -167,13 +167,19 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
val crossSigningEnabledOnAccount = myCrossSigningKeys != null val crossSigningEnabledOnAccount = myCrossSigningKeys != null
if (!crossSigningEnabledOnAccount && !sharedActionViewModel.isAccountCreation) { if (!crossSigningEnabledOnAccount && !sharedActionViewModel.isAccountCreation) {
// We need to ask // Do not propose for SSO accounts, because we do not support yet confirming account credentials using SSO
promptSecurityEvent( if (session.getHomeServerCapabilities().canChangePassword) {
session, // We need to ask
R.string.upgrade_security, promptSecurityEvent(
R.string.security_prompt_text session,
) { R.string.upgrade_security,
it.navigator.upgradeSessionSecurity(it) R.string.security_prompt_text
) {
it.navigator.upgradeSessionSecurity(it)
}
} else {
// Do not do it again
sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true
} }
} else if (myCrossSigningKeys?.isTrusted() == false) { } else if (myCrossSigningKeys?.isTrusted() == false) {
// We need to ask // We need to ask

View File

@ -37,7 +37,6 @@ class CrossSigningEpoxyController @Inject constructor(
interface InteractionListener { interface InteractionListener {
fun onInitializeCrossSigningKeys() fun onInitializeCrossSigningKeys()
fun onResetCrossSigningKeys()
fun verifySession() fun verifySession()
} }
@ -51,18 +50,6 @@ class CrossSigningEpoxyController @Inject constructor(
titleIconResourceId(R.drawable.ic_shield_trusted) titleIconResourceId(R.drawable.ic_shield_trusted)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete)) title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
} }
if (vectorPreferences.developerMode() && !data.isUploadingKeys) {
bottomSheetVerificationActionItem {
id("resetkeys")
title("Reset keys")
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
listener {
interactionListener?.onResetCrossSigningKeys()
}
}
}
} else if (data.xSigningKeysAreTrusted) { } else if (data.xSigningKeysAreTrusted) {
genericItem { genericItem {
id("trusted") id("trusted")
@ -70,19 +57,6 @@ class CrossSigningEpoxyController @Inject constructor(
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted)) title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
} }
if (!data.isUploadingKeys) { if (!data.isUploadingKeys) {
if (vectorPreferences.developerMode()) {
bottomSheetVerificationActionItem {
id("resetkeys")
title("Reset keys")
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
listener {
interactionListener?.onResetCrossSigningKeys()
}
}
}
bottomSheetVerificationActionItem { bottomSheetVerificationActionItem {
id("verify") id("verify")
title(stringProvider.getString(R.string.complete_security)) title(stringProvider.getString(R.string.complete_security))
@ -110,18 +84,6 @@ class CrossSigningEpoxyController @Inject constructor(
interactionListener?.verifySession() interactionListener?.verifySession()
} }
} }
if (vectorPreferences.developerMode()) {
bottomSheetVerificationActionItem {
id("resetkeys")
title("Reset keys")
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
listener {
interactionListener?.onResetCrossSigningKeys()
}
}
}
} else { } else {
genericItem { genericItem {
id("not") id("not")
@ -130,7 +92,7 @@ class CrossSigningEpoxyController @Inject constructor(
if (vectorPreferences.developerMode() && !data.isUploadingKeys) { if (vectorPreferences.developerMode() && !data.isUploadingKeys) {
bottomSheetVerificationActionItem { bottomSheetVerificationActionItem {
id("initKeys") id("initKeys")
title("Initialize keys") title(stringProvider.getString(R.string.initialize_cross_signing))
titleColor(colorProvider.getColor(R.color.riotx_positive_accent)) titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
iconRes(R.drawable.ic_arrow_right) iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_positive_accent)) iconColor(colorProvider.getColor(R.color.riotx_positive_accent))

View File

@ -101,14 +101,4 @@ class CrossSigningSettingsFragment @Inject constructor(
override fun verifySession() { override fun verifySession() {
viewModel.handle(CrossSigningAction.VerifySession) viewModel.handle(CrossSigningAction.VerifySession)
} }
override fun onResetCrossSigningKeys() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_title_confirmation)
.setMessage(R.string.are_you_sure)
.setPositiveButton(R.string.ok) { _, _ ->
viewModel.handle(CrossSigningAction.InitializeCrossSigning)
}
.show()
}
} }

View File

@ -22,14 +22,12 @@ import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
@ -113,35 +111,26 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
_pendingSession = null _pendingSession = null
if (failure is Failure.OtherServerError && failure.httpCode == 401) { val registrationFlowResponse = failure.toRegistrationFlowResponse()
try { if (registrationFlowResponse != null) {
MoshiProvider.providesMoshi() // Retry with authentication
.adapter(RegistrationFlowResponse::class.java) if (registrationFlowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } == true) {
.fromJson(failure.errorBody) _pendingSession = registrationFlowResponse.session ?: ""
} catch (e: Exception) { _viewEvents.post(CrossSigningSettingsViewEvents.RequestPassword)
null } else {
}?.let { flowResponse -> // can't do this from here
// Retry with authentication _viewEvents.post(CrossSigningSettingsViewEvents.Failure(Throwable("You cannot do that from mobile")))
if (flowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } == true) {
_pendingSession = flowResponse.session ?: ""
_viewEvents.post(CrossSigningSettingsViewEvents.RequestPassword)
return
} else {
// can't do this from here
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(Throwable("You cannot do that from mobile")))
setState { setState {
copy(isUploadingKeys = false) copy(isUploadingKeys = false)
}
return
} }
} }
} } else {
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(failure))
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(failure)) setState {
copy(isUploadingKeys = false)
setState { }
copy(isUploadingKeys = false)
} }
} }
}) })

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -12,10 +11,9 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp" android:paddingBottom="16dp">
android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/bootstrapIcon" android:id="@+id/bootstrapIcon"
@ -28,23 +26,19 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/bootstrapTitleText" android:id="@+id/bootstrapTitleText"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_weight="1"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2"
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/bootstrapIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/bootstrapIcon" app:layout_constraintStart_toEndOf="@+id/bootstrapIcon"
app:layout_constraintTop_toTopOf="@+id/bootstrapIcon" app:layout_constraintTop_toTopOf="parent"
tools:text="@string/recovery_passphrase" /> tools:text="@string/recovery_passphrase" />
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView

View File

@ -44,13 +44,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_weight="1"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" android:maxLines="2"
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/verificationRequestAvatar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/verificationRequestAvatar" app:layout_constraintStart_toEndOf="@+id/verificationRequestAvatar"
app:layout_constraintTop_toTopOf="@+id/verificationRequestAvatar" app:layout_constraintTop_toTopOf="@+id/verificationRequestAvatar"

View File

@ -12,11 +12,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
tools:text="@string/enter_account_password"
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/bootstrapAccountPasswordTil" app:layout_constraintBottom_toTopOf="@id/bootstrapAccountPasswordTil"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:text="@string/enter_account_password" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/bootstrapAccountPasswordTil" android:id="@+id/bootstrapAccountPasswordTil"
@ -59,9 +59,7 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/bootstrapPasswordButton" android:id="@+id/bootstrapPasswordButton"
style="@style/VectorButtonStyleText" style="@style/VectorButtonStyleText"
android:layout_gravity="end"
android:layout_marginTop="@dimen/layout_vertical_margin" android:layout_marginTop="@dimen/layout_vertical_margin"
android:padding="8dp"
android:text="@string/_continue" android:text="@string/_continue"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -25,7 +24,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:errorEnabled="true" app:errorEnabled="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ssss_view_show_password" app:layout_constraintEnd_toStartOf="@id/ssss_view_show_password"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bootstrapDescriptionText"> app:layout_constraintTop_toBottomOf="@id/bootstrapDescriptionText">
@ -34,11 +32,11 @@
android:id="@+id/ssss_passphrase_enter_edittext" android:id="@+id/ssss_passphrase_enter_edittext"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:hint="@string/passphrase_enter_passphrase"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:maxLines="3" android:maxLines="3"
android:singleLine="false" android:singleLine="false"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
tools:hint="@string/passphrase_enter_passphrase"
tools:inputType="textPassword" /> tools:inputType="textPassword" />
<!-- This is inside the TIL, if not the keyboard will hide it when in bottomsheet --> <!-- This is inside the TIL, if not the keyboard will hide it when in bottomsheet -->
@ -46,9 +44,9 @@
<im.vector.riotx.core.ui.views.PasswordStrengthBar <im.vector.riotx.core.ui.views.PasswordStrengthBar
android:id="@+id/ssss_passphrase_security_progress" android:id="@+id/ssss_passphrase_security_progress"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginBottom="2dp" android:layout_height="4dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:layout_height="4dp" /> android:layout_marginBottom="2dp" />
<TextView <TextView
android:id="@+id/bootstrapWarningInfo" android:id="@+id/bootstrapWarningInfo"
@ -56,12 +54,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:textSize="12sp"
android:gravity="center_vertical"
android:drawableStart="@drawable/ic_alert_triangle" android:drawableStart="@drawable/ic_alert_triangle"
android:drawableTint="@color/riotx_destructive_accent"
android:drawablePadding="4dp" android:drawablePadding="4dp"
android:text="@string/bootstrap_dont_reuse_pwd" /> android:drawableTint="@color/riotx_destructive_accent"
android:gravity="center_vertical"
android:text="@string/bootstrap_dont_reuse_pwd"
android:textSize="12sp" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -78,6 +76,13 @@
app:layout_constraintStart_toEndOf="@+id/ssss_passphrase_enter_til" app:layout_constraintStart_toEndOf="@+id/ssss_passphrase_enter_til"
app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" /> app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" />
<com.google.android.material.button.MaterialButton
android:id="@+id/bootstrapSubmit"
style="@style/VectorButtonStyleText"
android:text="@string/_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ssss_passphrase_enter_til" />
<!-- <TextView--> <!-- <TextView-->
<!-- android:id="@+id/bootstrapWarningInfo"--> <!-- android:id="@+id/bootstrapWarningInfo"-->

View File

@ -79,7 +79,6 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/bootstrapMigrateContinueButton" android:id="@+id/bootstrapMigrateContinueButton"
style="@style/VectorButtonStyleText" style="@style/VectorButtonStyleText"
android:layout_gravity="end"
android:layout_marginTop="@dimen/layout_vertical_margin" android:layout_marginTop="@dimen/layout_vertical_margin"
android:text="@string/_continue" android:text="@string/_continue"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"