mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Handle certificate error in case of Direct Login
This commit is contained in:
parent
e7f13c9efe
commit
e859357c6a
5 changed files with 104 additions and 34 deletions
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.auth.login
|
|||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||
import im.vector.matrix.android.internal.auth.SessionCreator
|
||||
|
@ -27,6 +28,7 @@ import im.vector.matrix.android.internal.di.Unauthenticated
|
|||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.network.httpclient.addSocketFactory
|
||||
import im.vector.matrix.android.internal.network.ssl.UnrecognizedCertificateException
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
|
@ -49,13 +51,28 @@ internal class DefaultDirectLoginTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: DirectLoginTask.Params): Session {
|
||||
val client = buildClient(params.homeServerConnectionConfig)
|
||||
val authAPI = retrofitFactory.create(client, params.homeServerConnectionConfig.homeServerUri.toString())
|
||||
val homeServerUrl = params.homeServerConnectionConfig.homeServerUri.toString()
|
||||
|
||||
val authAPI = retrofitFactory.create(client, homeServerUrl)
|
||||
.create(AuthAPI::class.java)
|
||||
|
||||
val loginParams = PasswordLoginParams.userIdentifier(params.userId, params.password, params.deviceName)
|
||||
|
||||
val credentials = executeRequest<Credentials>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
val credentials = try {
|
||||
executeRequest<Credentials>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
when (throwable) {
|
||||
is UnrecognizedCertificateException -> {
|
||||
throw Failure.UnrecognizedCertificateFailure(
|
||||
homeServerUrl,
|
||||
throwable.fingerprint
|
||||
)
|
||||
}
|
||||
else ->
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
|
||||
return sessionCreator.createSession(credentials, params.homeServerConnectionConfig)
|
||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.matrix.android.internal.di.Unauthenticated
|
|||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.network.httpclient.addSocketFactory
|
||||
import im.vector.matrix.android.internal.network.ssl.UnrecognizedCertificateException
|
||||
import im.vector.matrix.android.internal.session.homeserver.CapabilitiesAPI
|
||||
import im.vector.matrix.android.internal.session.identity.IdentityAuthAPI
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
|
@ -106,6 +107,12 @@ internal class DefaultGetWellknownTask @Inject constructor(
|
|||
}
|
||||
} catch (throwable: Throwable) {
|
||||
when (throwable) {
|
||||
is UnrecognizedCertificateException -> {
|
||||
throw Failure.UnrecognizedCertificateFailure(
|
||||
"https://$domain",
|
||||
throwable.fingerprint
|
||||
)
|
||||
}
|
||||
is Failure.NetworkConnection -> {
|
||||
WellknownResult.Ignore
|
||||
}
|
||||
|
|
|
@ -37,3 +37,11 @@ internal fun String.ensureProtocol(): String {
|
|||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
internal fun String.ensureTrailingSlash(): String {
|
||||
return when {
|
||||
isEmpty() -> this
|
||||
!endsWith("/") -> "$this/"
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
|
|||
loginServerUrlFormHomeServerUrlTil.hint = getText(R.string.login_server_url_form_modular_hint)
|
||||
loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_modular_notice)
|
||||
}
|
||||
ServerType.Other -> {
|
||||
else -> {
|
||||
loginServerUrlFormIcon.isVisible = false
|
||||
loginServerUrlFormTitle.text = getString(R.string.login_server_other_title)
|
||||
loginServerUrlFormText.text = getString(R.string.login_connect_to_a_custom_server)
|
||||
|
@ -78,7 +78,6 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
|
|||
loginServerUrlFormHomeServerUrlTil.hint = getText(R.string.login_server_url_form_other_hint)
|
||||
loginServerUrlFormNotice.text = getString(R.string.login_server_url_form_other_notice)
|
||||
}
|
||||
else -> error("This fragment should not be displayed in matrix.org mode")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
|||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||
import im.vector.matrix.android.api.auth.registration.Stage
|
||||
import im.vector.matrix.android.api.auth.wellknown.WellknownResult
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.riotx.R
|
||||
|
@ -47,6 +48,7 @@ import im.vector.riotx.core.extensions.configureAndStart
|
|||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.utils.ensureTrailingSlash
|
||||
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
|
||||
import im.vector.riotx.features.notifications.PushRuleTriggerListener
|
||||
import im.vector.riotx.features.session.SessionListener
|
||||
|
@ -87,8 +89,12 @@ class LoginViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// Store the last action, to redo it after user has trusted the untrusted certificate
|
||||
private var lastAction: LoginAction? = null
|
||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
||||
|
||||
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
||||
|
||||
val currentThreePid: String?
|
||||
get() = registrationWizard?.currentThreePid
|
||||
|
||||
|
@ -111,8 +117,8 @@ class LoginViewModel @AssistedInject constructor(
|
|||
is LoginAction.UpdateServerType -> handleUpdateServerType(action)
|
||||
is LoginAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||
is LoginAction.InitWith -> handleInitWith(action)
|
||||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action)
|
||||
is LoginAction.LoginOrRegister -> handleLoginOrRegister(action)
|
||||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
|
||||
is LoginAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
||||
is LoginAction.LoginWithToken -> handleLoginWithToken(action)
|
||||
is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is LoginAction.ResetPassword -> handleResetPassword(action)
|
||||
|
@ -126,10 +132,23 @@ class LoginViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun handleUserAcceptCertificate(action: LoginAction.UserAcceptCertificate) {
|
||||
// It happen when we get the login flow, so alter the homeserver config and retrieve again the login flow
|
||||
currentHomeServerConnectionConfig
|
||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
||||
?.let { getLoginFlow(it) }
|
||||
// It happen when we get the login flow, or during direct authentication.
|
||||
// So alter the homeserver config and retrieve again the login flow
|
||||
when (val finalLastAction = lastAction) {
|
||||
is LoginAction.UpdateHomeServer ->
|
||||
currentHomeServerConnectionConfig
|
||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
||||
?.let { getLoginFlow(it) }
|
||||
is LoginAction.LoginOrRegister ->
|
||||
handleDirectLogin(
|
||||
finalLastAction,
|
||||
HomeServerConnectionConfig.Builder()
|
||||
// Will be replaced by the task
|
||||
.withHomeServerUri("https://dummy.org")
|
||||
.withAllowedFingerPrints(listOf(action.fingerprint))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLoginWithToken(action: LoginAction.LoginWithToken) {
|
||||
|
@ -333,6 +352,7 @@ class LoginViewModel @AssistedInject constructor(
|
|||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||
homeServerUrl = null,
|
||||
loginMode = LoginMode.Unknown,
|
||||
serverType = ServerType.Unknown,
|
||||
loginModeSupportedTypes = emptyList()
|
||||
)
|
||||
}
|
||||
|
@ -395,7 +415,7 @@ class LoginViewModel @AssistedInject constructor(
|
|||
ServerType.Unknown -> Unit /* Should not happen */
|
||||
ServerType.MatrixOrg ->
|
||||
// Request login flow here
|
||||
handle(LoginAction.UpdateHomeServer(stringProvider.getString(R.string.matrix_org_server_url)))
|
||||
handle(LoginAction.UpdateHomeServer(matrixOrgUrl))
|
||||
ServerType.Modular,
|
||||
ServerType.Other -> _viewEvents.post(LoginViewEvents.OnServerSelectionDone)
|
||||
}.exhaustive
|
||||
|
@ -492,23 +512,22 @@ class LoginViewModel @AssistedInject constructor(
|
|||
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
||||
SignMode.SignIn -> handleLogin(action)
|
||||
SignMode.SignUp -> handleRegisterWith(action)
|
||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action)
|
||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleDirectLogin(action: LoginAction.LoginOrRegister) {
|
||||
private fun handleDirectLogin(action: LoginAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
// TODO Handle certificate error in this case. Direct login is deactivated now, so we will handle that later
|
||||
authenticationService.getWellKnownData(action.username, null, object : MatrixCallback<WellknownResult> {
|
||||
authenticationService.getWellKnownData(action.username, homeServerConnectionConfig, object : MatrixCallback<WellknownResult> {
|
||||
override fun onSuccess(data: WellknownResult) {
|
||||
when (data) {
|
||||
is WellknownResult.Prompt ->
|
||||
onWellknownSuccess(action, data)
|
||||
onWellknownSuccess(action, data, homeServerConnectionConfig)
|
||||
is WellknownResult.InvalidMatrixId -> {
|
||||
setState {
|
||||
copy(
|
||||
|
@ -529,23 +548,26 @@ class LoginViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
onDirectLoginError(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun onWellknownSuccess(action: LoginAction.LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt) {
|
||||
val homeServerConnectionConfig = HomeServerConnectionConfig(
|
||||
homeServerUri = Uri.parse(wellKnownPrompt.homeServerUrl),
|
||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
||||
)
|
||||
private fun onWellknownSuccess(action: LoginAction.LoginOrRegister,
|
||||
wellKnownPrompt: WellknownResult.Prompt,
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||
val alteredHomeServerConnectionConfig = homeServerConnectionConfig
|
||||
?.copy(
|
||||
homeServerUri = Uri.parse(wellKnownPrompt.homeServerUrl),
|
||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
||||
)
|
||||
?: HomeServerConnectionConfig(
|
||||
homeServerUri = Uri.parse(wellKnownPrompt.homeServerUrl),
|
||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
||||
)
|
||||
|
||||
authenticationService.directAuthentication(
|
||||
homeServerConnectionConfig,
|
||||
alteredHomeServerConnectionConfig,
|
||||
action.username,
|
||||
action.password,
|
||||
action.initialDeviceName,
|
||||
|
@ -555,15 +577,29 @@ class LoginViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
onDirectLoginError(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun onDirectLoginError(failure: Throwable) {
|
||||
if (failure is Failure.UnrecognizedCertificateFailure) {
|
||||
// Display this error in a dialog
|
||||
_viewEvents.post(LoginViewEvents.Failure(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Uninitialized
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLogin(action: LoginAction.LoginOrRegister) {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
|
@ -679,7 +715,10 @@ class LoginViewModel @AssistedInject constructor(
|
|||
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Loading()
|
||||
asyncHomeServerLoginFlowRequest = Loading(),
|
||||
// If user has entered https://matrix.org, ensure that server type is ServerType.MatrixOrg
|
||||
// It is also useful to set the value again in the case of a certificate error on matrix.org
|
||||
serverType = if (homeServerConnectionConfig.homeServerUri.toString() == matrixOrgUrl) ServerType.MatrixOrg else serverType
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue