mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 10:55:55 +03:00
Merge pull request #6873 from vector-im/feature/adm/allow-trusting-certificates
FTUE - Fix trusting certificates during edit server flow
This commit is contained in:
commit
9b57630eae
12 changed files with 207 additions and 48 deletions
1
changelog.d/6864.bugfix
Normal file
1
changelog.d/6864.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fixes server selection being unable to trust certificates
|
|
@ -93,6 +93,8 @@ fun Throwable.isMissingEmailVerification() = this is Failure.ServerError &&
|
||||||
error.code == MatrixError.M_UNAUTHORIZED &&
|
error.code == MatrixError.M_UNAUTHORIZED &&
|
||||||
error.message == "Unable to get validated threepid"
|
error.message == "Unable to get validated threepid"
|
||||||
|
|
||||||
|
fun Throwable.isUnrecognisedCertificate() = this is Failure.UnrecognizedCertificateFailure
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
* Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,12 +17,13 @@
|
||||||
package im.vector.app.features.login
|
package im.vector.app.features.login
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
import org.matrix.android.sdk.api.network.ssl.Fingerprint
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HomeServerConnectionConfigFactory @Inject constructor() {
|
class HomeServerConnectionConfigFactory @Inject constructor() {
|
||||||
|
|
||||||
fun create(url: String?): HomeServerConnectionConfig? {
|
fun create(url: String?, fingerprint: Fingerprint? = null): HomeServerConnectionConfig? {
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -30,6 +31,13 @@ class HomeServerConnectionConfigFactory @Inject constructor() {
|
||||||
return try {
|
return try {
|
||||||
HomeServerConnectionConfig.Builder()
|
HomeServerConnectionConfig.Builder()
|
||||||
.withHomeServerUri(url)
|
.withHomeServerUri(url)
|
||||||
|
.run {
|
||||||
|
if (fingerprint == null) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
withAllowedFingerPrints(listOf(fingerprint))
|
||||||
|
}
|
||||||
|
}
|
||||||
.build()
|
.build()
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Timber.e(t)
|
Timber.e(t)
|
||||||
|
|
|
@ -82,7 +82,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
|
||||||
|
|
||||||
data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction
|
data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction
|
||||||
|
|
||||||
data class UserAcceptCertificate(val fingerprint: Fingerprint) : OnboardingAction
|
data class UserAcceptCertificate(val fingerprint: Fingerprint, val retryAction: OnboardingAction) : OnboardingAction
|
||||||
|
|
||||||
object PersonalizeProfile : OnboardingAction
|
object PersonalizeProfile : OnboardingAction
|
||||||
data class UpdateDisplayName(val displayName: String) : OnboardingAction
|
data class UpdateDisplayName(val displayName: String) : OnboardingAction
|
||||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.app.core.platform.VectorViewEvents
|
||||||
import im.vector.app.features.login.ServerType
|
import im.vector.app.features.login.ServerType
|
||||||
import im.vector.app.features.login.SignMode
|
import im.vector.app.features.login.SignMode
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure as SdkFailure
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transient events for Login.
|
* Transient events for Login.
|
||||||
|
@ -29,6 +30,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {
|
||||||
data class Loading(val message: CharSequence? = null) : OnboardingViewEvents()
|
data class Loading(val message: CharSequence? = null) : OnboardingViewEvents()
|
||||||
data class Failure(val throwable: Throwable) : OnboardingViewEvents()
|
data class Failure(val throwable: Throwable) : OnboardingViewEvents()
|
||||||
data class DeeplinkAuthenticationFailure(val retryAction: OnboardingAction) : OnboardingViewEvents()
|
data class DeeplinkAuthenticationFailure(val retryAction: OnboardingAction) : OnboardingViewEvents()
|
||||||
|
data class UnrecognisedCertificateFailure(val retryAction: OnboardingAction, val cause: SdkFailure.UnrecognizedCertificateFailure) : OnboardingViewEvents()
|
||||||
|
|
||||||
object DisplayRegistrationFallback : OnboardingViewEvents()
|
object DisplayRegistrationFallback : OnboardingViewEvents()
|
||||||
data class DisplayRegistrationStage(val stage: Stage) : OnboardingViewEvents()
|
data class DisplayRegistrationStage(val stage: Stage) : OnboardingViewEvents()
|
||||||
|
|
|
@ -60,7 +60,10 @@ import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
|
import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
||||||
|
import org.matrix.android.sdk.api.failure.isUnrecognisedCertificate
|
||||||
|
import org.matrix.android.sdk.api.network.ssl.Fingerprint
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
|
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -113,10 +116,6 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the last action, to redo it after user has trusted the untrusted certificate
|
|
||||||
private var lastAction: OnboardingAction? = null
|
|
||||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
|
||||||
|
|
||||||
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
||||||
private val defaultHomeserverUrl = matrixOrgUrl
|
private val defaultHomeserverUrl = matrixOrgUrl
|
||||||
|
|
||||||
|
@ -146,9 +145,9 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
|
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
|
||||||
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
|
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||||
is OnboardingAction.InitWith -> handleInitWith(action)
|
is OnboardingAction.InitWith -> handleInitWith(action)
|
||||||
is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
|
is OnboardingAction.HomeServerChange -> handleHomeserverChange(action)
|
||||||
is OnboardingAction.UserNameEnteredAction -> handleUserNameEntered(action)
|
is OnboardingAction.UserNameEnteredAction -> handleUserNameEntered(action)
|
||||||
is AuthenticateAction -> withAction(action) { handleAuthenticateAction(action) }
|
is AuthenticateAction -> handleAuthenticateAction(action)
|
||||||
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
||||||
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||||
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
||||||
|
@ -221,11 +220,6 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withAction(action: OnboardingAction, block: (OnboardingAction) -> Unit) {
|
|
||||||
lastAction = action
|
|
||||||
block(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticateAction(action: AuthenticateAction) {
|
private fun handleAuthenticateAction(action: AuthenticateAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is AuthenticateAction.Register -> handleRegisterWith(action.username, action.password, action.initialDeviceName)
|
is AuthenticateAction.Register -> handleRegisterWith(action.username, action.password, action.initialDeviceName)
|
||||||
|
@ -276,20 +270,13 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
||||||
// It happens when we get the login flow, or during direct authentication.
|
// It happens when we get the login flow, or during direct authentication.
|
||||||
// So alter the homeserver config and retrieve again the login flow
|
// So alter the homeserver config and retrieve again the login flow
|
||||||
when (val finalLastAction = lastAction) {
|
when (action.retryAction) {
|
||||||
is OnboardingAction.HomeServerChange.SelectHomeServer -> {
|
is OnboardingAction.HomeServerChange -> handleHomeserverChange(action.retryAction, fingerprint = action.fingerprint)
|
||||||
currentHomeServerConnectionConfig
|
|
||||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
|
||||||
?.let { startAuthenticationFlow(finalLastAction, it, serverTypeOverride = null) }
|
|
||||||
}
|
|
||||||
is AuthenticateAction.LoginDirect ->
|
is AuthenticateAction.LoginDirect ->
|
||||||
handleDirectLogin(
|
handleDirectLogin(
|
||||||
finalLastAction,
|
action.retryAction,
|
||||||
HomeServerConnectionConfig.Builder()
|
// Will be replaced by the task
|
||||||
// Will be replaced by the task
|
homeServerConnectionConfigFactory.create("https://dummy.org", action.fingerprint)
|
||||||
.withHomeServerUri("https://dummy.org")
|
|
||||||
.withAllowedFingerPrints(listOf(action.fingerprint))
|
|
||||||
.build()
|
|
||||||
)
|
)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
@ -589,9 +576,19 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
|
directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
|
||||||
onSuccess = { onSessionCreated(it, authenticationDescription = AuthenticationDescription.Login) },
|
onSuccess = { onSessionCreated(it, authenticationDescription = AuthenticationDescription.Login) },
|
||||||
onFailure = {
|
onFailure = { error ->
|
||||||
setState { copy(isLoading = false) }
|
setState { copy(isLoading = false) }
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(it))
|
when {
|
||||||
|
error.isUnrecognisedCertificate() -> {
|
||||||
|
_viewEvents.post(
|
||||||
|
OnboardingViewEvents.UnrecognisedCertificateFailure(
|
||||||
|
retryAction = action,
|
||||||
|
cause = error as Failure.UnrecognizedCertificateFailure
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> _viewEvents.post(OnboardingViewEvents.Failure(error))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -682,8 +679,13 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleHomeserverChange(action: OnboardingAction.HomeServerChange, serverTypeOverride: ServerType? = null, postAction: suspend () -> Unit = {}) {
|
private fun handleHomeserverChange(
|
||||||
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl)
|
action: OnboardingAction.HomeServerChange,
|
||||||
|
serverTypeOverride: ServerType? = null,
|
||||||
|
fingerprint: Fingerprint? = null,
|
||||||
|
postAction: suspend () -> Unit = {},
|
||||||
|
) {
|
||||||
|
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl, fingerprint)
|
||||||
if (homeServerConnectionConfig == null) {
|
if (homeServerConnectionConfig == null) {
|
||||||
// This is invalid
|
// This is invalid
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
|
_viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
|
||||||
|
@ -698,8 +700,6 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
serverTypeOverride: ServerType?,
|
serverTypeOverride: ServerType?,
|
||||||
postAction: suspend () -> Unit = {},
|
postAction: suspend () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
currentHomeServerConnectionConfig = homeServerConnectionConfig
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
setState { copy(isLoading = true) }
|
setState { copy(isLoading = true) }
|
||||||
runCatching { startAuthenticationFlowUseCase.execute(homeServerConnectionConfig) }.fold(
|
runCatching { startAuthenticationFlowUseCase.execute(homeServerConnectionConfig) }.fold(
|
||||||
|
@ -723,9 +723,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
retryAction = (trigger as OnboardingAction.HomeServerChange.SelectHomeServer).resetToDefaultUrl()
|
retryAction = (trigger as OnboardingAction.HomeServerChange.SelectHomeServer).resetToDefaultUrl()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else -> _viewEvents.post(
|
error.isUnrecognisedCertificate() -> {
|
||||||
OnboardingViewEvents.Failure(error)
|
_viewEvents.post(OnboardingViewEvents.UnrecognisedCertificateFailure(trigger, error as Failure.UnrecognizedCertificateFailure))
|
||||||
)
|
}
|
||||||
|
else -> _viewEvents.post(OnboardingViewEvents.Failure(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@ import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
import im.vector.app.features.onboarding.OnboardingViewModel
|
import im.vector.app.features.onboarding.OnboardingViewModel
|
||||||
import im.vector.app.features.onboarding.OnboardingViewState
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parent Fragment for all the login/registration screens.
|
* Parent Fragment for all the login/registration screens.
|
||||||
|
@ -68,6 +67,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
|
||||||
private fun handleOnboardingViewEvents(viewEvents: OnboardingViewEvents) {
|
private fun handleOnboardingViewEvents(viewEvents: OnboardingViewEvents) {
|
||||||
when (viewEvents) {
|
when (viewEvents) {
|
||||||
is OnboardingViewEvents.Failure -> showFailure(viewEvents.throwable)
|
is OnboardingViewEvents.Failure -> showFailure(viewEvents.throwable)
|
||||||
|
is OnboardingViewEvents.UnrecognisedCertificateFailure -> showUnrecognizedCertificateFailure(viewEvents)
|
||||||
else ->
|
else ->
|
||||||
// This is handled by the Activity
|
// This is handled by the Activity
|
||||||
Unit
|
Unit
|
||||||
|
@ -84,20 +84,20 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
|
||||||
is CancellationException ->
|
is CancellationException ->
|
||||||
/* Ignore this error, user has cancelled the action */
|
/* Ignore this error, user has cancelled the action */
|
||||||
Unit
|
Unit
|
||||||
is Failure.UnrecognizedCertificateFailure -> showUnrecognizedCertificateFailure(throwable)
|
|
||||||
else -> onError(throwable)
|
else -> onError(throwable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUnrecognizedCertificateFailure(failure: Failure.UnrecognizedCertificateFailure) {
|
private fun showUnrecognizedCertificateFailure(event: OnboardingViewEvents.UnrecognisedCertificateFailure) {
|
||||||
// Ask the user to accept the certificate
|
// Ask the user to accept the certificate
|
||||||
|
val cause = event.cause
|
||||||
unrecognizedCertificateDialog.show(requireActivity(),
|
unrecognizedCertificateDialog.show(requireActivity(),
|
||||||
failure.fingerprint,
|
cause.fingerprint,
|
||||||
failure.url,
|
cause.url,
|
||||||
object : UnrecognizedCertificateDialog.Callback {
|
object : UnrecognizedCertificateDialog.Callback {
|
||||||
override fun onAccept() {
|
override fun onAccept() {
|
||||||
// User accept the certificate
|
// User accept the certificate
|
||||||
viewModel.handle(OnboardingAction.UserAcceptCertificate(failure.fingerprint))
|
viewModel.handle(OnboardingAction.UserAcceptCertificate(cause.fingerprint, event.retryAction))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIgnore() {
|
override fun onIgnore() {
|
||||||
|
|
|
@ -202,6 +202,7 @@ class FtueAuthVariant(
|
||||||
openMsisdnConfirmation(viewEvents.msisdn)
|
openMsisdnConfirmation(viewEvents.msisdn)
|
||||||
}
|
}
|
||||||
is OnboardingViewEvents.Failure,
|
is OnboardingViewEvents.Failure,
|
||||||
|
is OnboardingViewEvents.UnrecognisedCertificateFailure,
|
||||||
is OnboardingViewEvents.Loading ->
|
is OnboardingViewEvents.Loading ->
|
||||||
// This is handled by the Fragments
|
// This is handled by the Fragments
|
||||||
Unit
|
Unit
|
||||||
|
|
|
@ -48,6 +48,7 @@ import im.vector.app.test.fakes.FakeVectorOverrides
|
||||||
import im.vector.app.test.fakes.toTestString
|
import im.vector.app.test.fakes.toTestString
|
||||||
import im.vector.app.test.fixtures.a401ServerError
|
import im.vector.app.test.fixtures.a401ServerError
|
||||||
import im.vector.app.test.fixtures.aHomeServerCapabilities
|
import im.vector.app.test.fixtures.aHomeServerCapabilities
|
||||||
|
import im.vector.app.test.fixtures.anUnrecognisedCertificateError
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
@ -58,6 +59,7 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.network.ssl.Fingerprint
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||||
|
|
||||||
|
@ -65,10 +67,12 @@ private const val A_DISPLAY_NAME = "a display name"
|
||||||
private const val A_PICTURE_FILENAME = "a-picture.png"
|
private const val A_PICTURE_FILENAME = "a-picture.png"
|
||||||
private val A_SERVER_ERROR = a401ServerError()
|
private val A_SERVER_ERROR = a401ServerError()
|
||||||
private val AN_ERROR = RuntimeException("an error!")
|
private val AN_ERROR = RuntimeException("an error!")
|
||||||
|
private val AN_UNRECOGNISED_CERTIFICATE_ERROR = anUnrecognisedCertificateError()
|
||||||
private val A_LOADABLE_REGISTER_ACTION = RegisterAction.StartRegistration
|
private val A_LOADABLE_REGISTER_ACTION = RegisterAction.StartRegistration
|
||||||
private val A_NON_LOADABLE_REGISTER_ACTION = RegisterAction.CheckIfEmailHasBeenValidated(delayMillis = -1L)
|
private val A_NON_LOADABLE_REGISTER_ACTION = RegisterAction.CheckIfEmailHasBeenValidated(delayMillis = -1L)
|
||||||
private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.SendAgainThreePid
|
private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.SendAgainThreePid
|
||||||
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
||||||
|
private val A_FINGERPRINT = Fingerprint(ByteArray(1), Fingerprint.HashType.SHA1)
|
||||||
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationActionHandler.Result.NextStage(Stage.Dummy(mandatory = true))
|
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationActionHandler.Result.NextStage(Stage.Dummy(mandatory = true))
|
||||||
private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
||||||
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
||||||
|
@ -320,6 +324,25 @@ class OnboardingViewModelTest {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given has sign in with matrix id sign mode, when handling login or register action fails with certificate error, then emits error`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
|
||||||
|
fakeDirectLoginUseCase.givenFailureResult(A_DIRECT_LOGIN, config = null, cause = AN_UNRECOGNISED_CERTIFICATE_ERROR)
|
||||||
|
givenInitialisesSession(fakeSession)
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(A_DIRECT_LOGIN)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(A_DIRECT_LOGIN, AN_UNRECOGNISED_CERTIFICATE_ERROR))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when handling SignUp then sets sign mode to sign up and starts registration`() = runTest {
|
fun `when handling SignUp then sets sign mode to sign up and starts registration`() = runTest {
|
||||||
givenRegistrationResultFor(RegisterAction.StartRegistration, ANY_CONTINUING_REGISTRATION_RESULT)
|
givenRegistrationResultFor(RegisterAction.StartRegistration, ANY_CONTINUING_REGISTRATION_RESULT)
|
||||||
|
@ -406,7 +429,7 @@ class OnboardingViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given unavailable deeplink, when selecting homeserver, then emits failure with default homeserver as retry action`() = runTest {
|
fun `given unavailable deeplink, when selecting homeserver, then emits failure with default homeserver as retry action`() = runTest {
|
||||||
fakeContext.givenHasConnection()
|
fakeContext.givenHasConnection()
|
||||||
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, A_HOMESERVER_CONFIG)
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
|
||||||
fakeStartAuthenticationFlowUseCase.givenHomeserverUnavailable(A_HOMESERVER_CONFIG)
|
fakeStartAuthenticationFlowUseCase.givenHomeserverUnavailable(A_HOMESERVER_CONFIG)
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
@ -548,6 +571,44 @@ class OnboardingViewModelTest {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when editing homeserver errors with certificate error, then emits error`() = runTest {
|
||||||
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
|
||||||
|
fakeStartAuthenticationFlowUseCase.givenErrors(A_HOMESERVER_CONFIG, AN_UNRECOGNISED_CERTIFICATE_ERROR)
|
||||||
|
val editAction = OnboardingAction.HomeServerChange.EditHomeServer(A_HOMESERVER_URL)
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(editAction)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(editAction, AN_UNRECOGNISED_CERTIFICATE_ERROR))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when selecting homeserver errors with certificate error, then emits error`() = runTest {
|
||||||
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, fingerprint = null, A_HOMESERVER_CONFIG)
|
||||||
|
fakeStartAuthenticationFlowUseCase.givenErrors(A_HOMESERVER_CONFIG, AN_UNRECOGNISED_CERTIFICATE_ERROR)
|
||||||
|
val selectAction = OnboardingAction.HomeServerChange.SelectHomeServer(A_HOMESERVER_URL)
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(selectAction)
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.UnrecognisedCertificateFailure(selectAction, AN_UNRECOGNISED_CERTIFICATE_ERROR))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given unavailable full matrix id, when a register username is entered, then emits availability error`() = runTest {
|
fun `given unavailable full matrix id, when a register username is entered, then emits availability error`() = runTest {
|
||||||
viewModelWith(initialRegistrationState("ignored-url"))
|
viewModelWith(initialRegistrationState("ignored-url"))
|
||||||
|
@ -724,6 +785,76 @@ class OnboardingViewModelTest {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given in sign in mode, when accepting user certificate with SelectHomeserver retry action, then emits OnHomeserverEdited`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignIn))
|
||||||
|
val test = viewModel.test()
|
||||||
|
fakeVectorFeatures.givenCombinedLoginEnabled()
|
||||||
|
givenCanSuccessfullyUpdateHomeserver(
|
||||||
|
A_HOMESERVER_URL,
|
||||||
|
SELECTED_HOMESERVER_STATE,
|
||||||
|
config = A_HOMESERVER_CONFIG.copy(allowedFingerprints = listOf(A_FINGERPRINT)),
|
||||||
|
fingerprint = A_FINGERPRINT,
|
||||||
|
)
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, OnboardingAction.HomeServerChange.SelectHomeServer(A_HOMESERVER_URL)))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(selectedHomeserver = SELECTED_HOMESERVER_STATE) },
|
||||||
|
{ copy(signMode = SignMode.SignIn) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OpenCombinedLogin)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given in sign up mode, when accepting user certificate with EditHomeserver retry action, then emits OnHomeserverEdited`() = runTest {
|
||||||
|
viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignUp))
|
||||||
|
givenCanSuccessfullyUpdateHomeserver(
|
||||||
|
A_HOMESERVER_URL,
|
||||||
|
SELECTED_HOMESERVER_STATE,
|
||||||
|
config = A_HOMESERVER_CONFIG.copy(allowedFingerprints = listOf(A_FINGERPRINT)),
|
||||||
|
fingerprint = A_FINGERPRINT,
|
||||||
|
)
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, OnboardingAction.HomeServerChange.EditHomeServer(A_HOMESERVER_URL)))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(selectedHomeserver = SELECTED_HOMESERVER_STATE) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OnHomeserverEdited)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given DirectLogin retry action, when accepting user certificate, then logs in directly`() = runTest {
|
||||||
|
fakeHomeServerConnectionConfigFactory.givenConfigFor("https://dummy.org", A_FINGERPRINT, A_HOMESERVER_CONFIG)
|
||||||
|
fakeDirectLoginUseCase.givenSuccessResult(A_DIRECT_LOGIN, config = A_HOMESERVER_CONFIG, result = fakeSession)
|
||||||
|
givenInitialisesSession(fakeSession)
|
||||||
|
val test = viewModel.test()
|
||||||
|
|
||||||
|
viewModel.handle(OnboardingAction.UserAcceptCertificate(A_FINGERPRINT, A_DIRECT_LOGIN))
|
||||||
|
|
||||||
|
test
|
||||||
|
.assertStatesChanges(
|
||||||
|
initialState,
|
||||||
|
{ copy(isLoading = true) },
|
||||||
|
{ copy(isLoading = false) }
|
||||||
|
)
|
||||||
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given can successfully start password reset, when resetting password, then emits confirmation email sent`() = runTest {
|
fun `given can successfully start password reset, when resetting password, then emits confirmation email sent`() = runTest {
|
||||||
viewModelWith(initialState.copy(selectedHomeserver = SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES))
|
viewModelWith(initialState.copy(selectedHomeserver = SELECTED_HOMESERVER_STATE_SUPPORTED_LOGOUT_DEVICES))
|
||||||
|
@ -991,15 +1122,20 @@ class OnboardingViewModelTest {
|
||||||
fakeRegistrationActionHandler.givenResultsFor(results)
|
fakeRegistrationActionHandler.givenResultsFor(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun givenCanSuccessfullyUpdateHomeserver(homeserverUrl: String, resultingState: SelectedHomeserverState) {
|
private fun givenCanSuccessfullyUpdateHomeserver(
|
||||||
fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, A_HOMESERVER_CONFIG)
|
homeserverUrl: String,
|
||||||
fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
|
resultingState: SelectedHomeserverState,
|
||||||
|
config: HomeServerConnectionConfig = A_HOMESERVER_CONFIG,
|
||||||
|
fingerprint: Fingerprint? = null,
|
||||||
|
) {
|
||||||
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, fingerprint, config)
|
||||||
|
fakeStartAuthenticationFlowUseCase.givenResult(config, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
|
||||||
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.StartRegistration)
|
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.StartRegistration)
|
||||||
fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
|
fakeHomeServerHistoryService.expectUrlToBeAdded(config.homeServerUri.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun givenUpdatingHomeserverErrors(homeserverUrl: String, resultingState: SelectedHomeserverState, error: Throwable) {
|
private fun givenUpdatingHomeserverErrors(homeserverUrl: String, resultingState: SelectedHomeserverState, error: Throwable) {
|
||||||
fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, A_HOMESERVER_CONFIG)
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(homeserverUrl, fingerprint = null, A_HOMESERVER_CONFIG)
|
||||||
fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
|
fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, resultingState))
|
||||||
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.Error(error))
|
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationActionHandler.Result.Error(error))
|
||||||
fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
|
fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
|
||||||
|
|
|
@ -20,11 +20,12 @@ import im.vector.app.features.login.HomeServerConnectionConfigFactory
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
import org.matrix.android.sdk.api.network.ssl.Fingerprint
|
||||||
|
|
||||||
class FakeHomeServerConnectionConfigFactory {
|
class FakeHomeServerConnectionConfigFactory {
|
||||||
val instance: HomeServerConnectionConfigFactory = mockk()
|
val instance: HomeServerConnectionConfigFactory = mockk()
|
||||||
|
|
||||||
fun givenConfigFor(url: String, config: HomeServerConnectionConfig) {
|
fun givenConfigFor(url: String, fingerprint: Fingerprint? = null, config: HomeServerConnectionConfig) {
|
||||||
every { instance.create(url) } returns config
|
every { instance.create(url, fingerprint) } returns config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@ class FakeStartAuthenticationFlowUseCase {
|
||||||
coEvery { instance.execute(config) } returns result
|
coEvery { instance.execute(config) } returns result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenErrors(config: HomeServerConnectionConfig, error: Throwable) {
|
||||||
|
coEvery { instance.execute(config) } throws error
|
||||||
|
}
|
||||||
|
|
||||||
fun givenHomeserverUnavailable(config: HomeServerConnectionConfig) {
|
fun givenHomeserverUnavailable(config: HomeServerConnectionConfig) {
|
||||||
coEvery { instance.execute(config) } throws aHomeserverUnavailableError()
|
coEvery { instance.execute(config) } throws aHomeserverUnavailableError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.test.fixtures
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.MatrixError
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
|
import org.matrix.android.sdk.api.network.ssl.Fingerprint
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
|
@ -38,3 +39,5 @@ fun aLoginEmailUnknownError() = Failure.ServerError(
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aHomeserverUnavailableError() = Failure.NetworkConnection(UnknownHostException())
|
fun aHomeserverUnavailableError() = Failure.NetworkConnection(UnknownHostException())
|
||||||
|
|
||||||
|
fun anUnrecognisedCertificateError() = Failure.UnrecognizedCertificateFailure("a-url", Fingerprint(ByteArray(1), Fingerprint.HashType.SHA1))
|
||||||
|
|
Loading…
Reference in a new issue