Store homeServerUriBase independently that the base URL for client-server API

Also handle the migration for the specific matrix-client.matrix.org URL
This commit is contained in:
Benoit Marty 2021-06-30 10:56:15 +02:00
parent 0f25e2986f
commit 38cb8bd33e
15 changed files with 85 additions and 43 deletions

View file

@ -30,8 +30,13 @@ import okhttp3.TlsVersion
* You should use the [Builder] to create one. * You should use the [Builder] to create one.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class HomeServerConnectionConfig( data class HomeServerConnectionConfig constructor(
// This is the homeserver URL entered by the user
val homeServerUri: Uri, val homeServerUri: Uri,
// This is the homeserver base URL for the client-server API. Default to homeServerUri,
// but can be updated with data from .Well-Known before login, and/or with the data
// included in the login response
val homeServerUriBase: Uri = homeServerUri,
val identityServerUri: Uri? = null, val identityServerUri: Uri? = null,
val antiVirusServerUri: Uri? = null, val antiVirusServerUri: Uri? = null,
val allowedFingerprints: List<Fingerprint> = emptyList(), val allowedFingerprints: List<Fingerprint> = emptyList(),
@ -47,7 +52,6 @@ data class HomeServerConnectionConfig(
* This builder should be use to create a [HomeServerConnectionConfig] instance. * This builder should be use to create a [HomeServerConnectionConfig] instance.
*/ */
class Builder { class Builder {
private lateinit var homeServerUri: Uri private lateinit var homeServerUri: Uri
private var identityServerUri: Uri? = null private var identityServerUri: Uri? = null
private var antiVirusServerUri: Uri? = null private var antiVirusServerUri: Uri? = null
@ -234,16 +238,16 @@ data class HomeServerConnectionConfig(
*/ */
fun build(): HomeServerConnectionConfig { fun build(): HomeServerConnectionConfig {
return HomeServerConnectionConfig( return HomeServerConnectionConfig(
homeServerUri, homeServerUri = homeServerUri,
identityServerUri, identityServerUri = identityServerUri,
antiVirusServerUri, antiVirusServerUri = antiVirusServerUri,
allowedFingerprints, allowedFingerprints = allowedFingerprints,
shouldPin, shouldPin = shouldPin,
tlsVersions, tlsVersions = tlsVersions,
tlsCipherSuites, tlsCipherSuites = tlsCipherSuites,
shouldAcceptTlsExtensions, shouldAcceptTlsExtensions = shouldAcceptTlsExtensions,
allowHttpExtension, allowHttpExtension = allowHttpExtension,
forceUsageTlsVersions forceUsageTlsVersions = forceUsageTlsVersions
) )
} }
} }

View file

@ -51,13 +51,18 @@ data class SessionParams(
val deviceId = credentials.deviceId val deviceId = credentials.deviceId
/** /**
* The current homeserver Url. It can be different that the homeserver url entered * The homeserver Url entered by the user during the login phase.
* during login phase, because a redirection may have occurred
*/ */
val homeServerUrl = homeServerConnectionConfig.homeServerUri.toString() val homeServerUrl = homeServerConnectionConfig.homeServerUri.toString()
/** /**
* The current homeserver host * The current homeserver Url for client-server API. It can be different that the homeserver url entered
* during login phase, because a redirection may have occurred
*/
val homeServerUrlBase = homeServerConnectionConfig.homeServerUriBase.toString()
/**
* The current homeserver host, using what has been entered by the user during login phase
*/ */
val homeServerHost = homeServerConnectionConfig.homeServerUri.host val homeServerHost = homeServerConnectionConfig.homeServerUri.host

View file

@ -124,7 +124,7 @@ internal class DefaultAuthenticationService @Inject constructor(
private fun getHomeServerUrlBase(): String? { private fun getHomeServerUrlBase(): String? {
return pendingSessionData return pendingSessionData
?.homeServerConnectionConfig ?.homeServerConnectionConfig
?.homeServerUri ?.homeServerUriBase
?.toString() ?.toString()
?.trim { it == '/' } ?.trim { it == '/' }
} }
@ -147,7 +147,7 @@ internal class DefaultAuthenticationService @Inject constructor(
// The homeserver exists and up to date, keep the config // The homeserver exists and up to date, keep the config
// Homeserver url may have been changed, if it was a Riot url // Homeserver url may have been changed, if it was a Riot url
val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy( val alteredHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(it.homeServerUrl) homeServerUriBase = Uri.parse(it.homeServerUrl)
) )
pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig) pendingSessionData = PendingSessionData(alteredHomeServerConnectionConfig)
@ -156,7 +156,7 @@ internal class DefaultAuthenticationService @Inject constructor(
}, },
{ {
if (it is UnrecognizedCertificateException) { if (it is UnrecognizedCertificateException) {
throw Failure.UnrecognizedCertificateFailure(homeServerConnectionConfig.homeServerUri.toString(), it.fingerprint) throw Failure.UnrecognizedCertificateFailure(homeServerConnectionConfig.homeServerUriBase.toString(), it.fingerprint)
} else { } else {
throw it throw it
} }
@ -175,7 +175,7 @@ internal class DefaultAuthenticationService @Inject constructor(
} }
.map { versions -> .map { versions ->
// Ok, it seems that the homeserver url is valid // Ok, it seems that the homeserver url is valid
getLoginFlowResult(authAPI, versions, homeServerConnectionConfig.homeServerUri.toString()) getLoginFlowResult(authAPI, versions, homeServerConnectionConfig.homeServerUriBase.toString())
} }
.fold( .fold(
{ {
@ -196,7 +196,7 @@ internal class DefaultAuthenticationService @Inject constructor(
private suspend fun getRiotDomainLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { private suspend fun getRiotDomainLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult {
val authAPI = buildAuthAPI(homeServerConnectionConfig) val authAPI = buildAuthAPI(homeServerConnectionConfig)
val domain = homeServerConnectionConfig.homeServerUri.host val domain = homeServerConnectionConfig.homeServerUriBase.host
?: return getRiotLoginFlowInternal(homeServerConnectionConfig) ?: return getRiotLoginFlowInternal(homeServerConnectionConfig)
// Ok, try to get the config.domain.json file of a RiotWeb client // Ok, try to get the config.domain.json file of a RiotWeb client
@ -257,7 +257,7 @@ internal class DefaultAuthenticationService @Inject constructor(
if (defaultHomeServerUrl?.isNotEmpty() == true) { if (defaultHomeServerUrl?.isNotEmpty() == true) {
// Ok, good sign, we got a default hs url // Ok, good sign, we got a default hs url
val newHomeServerConnectionConfig = homeServerConnectionConfig.copy( val newHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(defaultHomeServerUrl) homeServerUriBase = Uri.parse(defaultHomeServerUrl)
) )
val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig) val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig)
@ -274,7 +274,7 @@ internal class DefaultAuthenticationService @Inject constructor(
} }
private suspend fun getWellknownLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { private suspend fun getWellknownLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult {
val domain = homeServerConnectionConfig.homeServerUri.host val domain = homeServerConnectionConfig.homeServerUriBase.host
?: throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) ?: throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */)
val wellknownResult = getWellknownTask.execute(GetWellknownTask.Params(domain, homeServerConnectionConfig)) val wellknownResult = getWellknownTask.execute(GetWellknownTask.Params(domain, homeServerConnectionConfig))
@ -282,7 +282,7 @@ internal class DefaultAuthenticationService @Inject constructor(
return when (wellknownResult) { return when (wellknownResult) {
is WellknownResult.Prompt -> { is WellknownResult.Prompt -> {
val newHomeServerConnectionConfig = homeServerConnectionConfig.copy( val newHomeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = Uri.parse(wellknownResult.homeServerUrl), homeServerUriBase = Uri.parse(wellknownResult.homeServerUrl),
identityServerUri = wellknownResult.identityServerUrl?.let { Uri.parse(it) } identityServerUri = wellknownResult.identityServerUrl?.let { Uri.parse(it) }
) )
@ -397,7 +397,7 @@ internal class DefaultAuthenticationService @Inject constructor(
} }
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUri.toString()) val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
return retrofit.create(AuthAPI::class.java) return retrofit.create(AuthAPI::class.java)
} }

View file

@ -42,7 +42,7 @@ internal class DefaultIsValidClientServerApiTask @Inject constructor(
override suspend fun execute(params: IsValidClientServerApiTask.Params): Boolean { override suspend fun execute(params: IsValidClientServerApiTask.Params): Boolean {
val client = buildClient(params.homeServerConnectionConfig) val client = buildClient(params.homeServerConnectionConfig)
val homeServerUrl = params.homeServerConnectionConfig.homeServerUri.toString() val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
val authAPI = retrofitFactory.create(client, homeServerUrl) val authAPI = retrofitFactory.create(client, homeServerUrl)
.create(AuthAPI::class.java) .create(AuthAPI::class.java)

View file

@ -56,7 +56,7 @@ internal class DefaultSessionCreator @Inject constructor(
tryOrNull { tryOrNull {
isValidClientServerApiTask.execute( isValidClientServerApiTask.execute(
IsValidClientServerApiTask.Params( IsValidClientServerApiTask.Params(
homeServerConnectionConfig.copy(homeServerUri = it) homeServerConnectionConfig.copy(homeServerUriBase = it)
) )
) )
.also { Timber.d("Overriding homeserver url: $it") } .also { Timber.d("Overriding homeserver url: $it") }
@ -66,7 +66,7 @@ internal class DefaultSessionCreator @Inject constructor(
val sessionParams = SessionParams( val sessionParams = SessionParams(
credentials = credentials, credentials = credentials,
homeServerConnectionConfig = homeServerConnectionConfig.copy( homeServerConnectionConfig = homeServerConnectionConfig.copy(
homeServerUri = overriddenUrl ?: homeServerConnectionConfig.homeServerUri, homeServerUriBase = overriddenUrl ?: homeServerConnectionConfig.homeServerUriBase,
identityServerUri = credentials.discoveryInformation?.identityServer?.baseURL identityServerUri = credentials.discoveryInformation?.identityServer?.baseURL
// remove trailing "/" // remove trailing "/"
?.trim { it == '/' } ?.trim { it == '/' }

View file

@ -16,17 +16,19 @@
package org.matrix.android.sdk.internal.auth.db package org.matrix.android.sdk.internal.auth.db
import org.matrix.android.sdk.api.auth.data.Credentials import android.net.Uri
import org.matrix.android.sdk.api.auth.data.sessionId
import org.matrix.android.sdk.internal.di.MoshiProvider
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.RealmMigration import io.realm.RealmMigration
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.sessionId
import org.matrix.android.sdk.internal.di.MoshiProvider
import timber.log.Timber import timber.log.Timber
internal object AuthRealmMigration : RealmMigration { internal object AuthRealmMigration : RealmMigration {
// Current schema version // Current schema version
const val SCHEMA_VERSION = 3L const val SCHEMA_VERSION = 4L
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
Timber.d("Migrating Auth Realm from $oldVersion to $newVersion") Timber.d("Migrating Auth Realm from $oldVersion to $newVersion")
@ -34,6 +36,7 @@ internal object AuthRealmMigration : RealmMigration {
if (oldVersion <= 0) migrateTo1(realm) if (oldVersion <= 0) migrateTo1(realm)
if (oldVersion <= 1) migrateTo2(realm) if (oldVersion <= 1) migrateTo2(realm)
if (oldVersion <= 2) migrateTo3(realm) if (oldVersion <= 2) migrateTo3(realm)
if (oldVersion <= 3) migrateTo4(realm)
} }
private fun migrateTo1(realm: DynamicRealm) { private fun migrateTo1(realm: DynamicRealm) {
@ -81,4 +84,34 @@ internal object AuthRealmMigration : RealmMigration {
} }
?.addPrimaryKey(SessionParamsEntityFields.SESSION_ID) ?.addPrimaryKey(SessionParamsEntityFields.SESSION_ID)
} }
private fun migrateTo4(realm: DynamicRealm) {
Timber.d("Step 3 -> 4")
Timber.d("Update SessionParamsEntity to add HomeServerConnectionConfig.homeServerUriBase value")
val adapter = MoshiProvider.providesMoshi()
.adapter(HomeServerConnectionConfig::class.java)
realm.schema.get("SessionParamsEntity")
?.transform {
val homeserverConnectionConfigJson = it.getString(SessionParamsEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON)
val homeserverConnectionConfig = adapter
.fromJson(homeserverConnectionConfigJson)
val homeserverUrl = homeserverConnectionConfig?.homeServerUri?.toString()
// Special case for matrix.org. Old session may use "https://matrix.org", newer one may use
// "https://matrix-client.matrix.org". So fix that here
val alteredHomeserverConnectionConfig =
if (homeserverUrl == "https://matrix.org" || homeserverUrl == "https://matrix-client.matrix.org") {
homeserverConnectionConfig.copy(
homeServerUri = Uri.parse("https://matrix.org"),
homeServerUriBase = Uri.parse("https://matrix-client.matrix.org")
)
} else {
homeserverConnectionConfig
}
it.set(SessionParamsEntityFields.HOME_SERVER_CONNECTION_CONFIG_JSON, adapter.toJson(alteredHomeserverConnectionConfig))
}
}
} }

View file

@ -50,7 +50,7 @@ internal class DefaultDirectLoginTask @Inject constructor(
override suspend fun execute(params: DirectLoginTask.Params): Session { override suspend fun execute(params: DirectLoginTask.Params): Session {
val client = buildClient(params.homeServerConnectionConfig) val client = buildClient(params.homeServerConnectionConfig)
val homeServerUrl = params.homeServerConnectionConfig.homeServerUri.toString() val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
val authAPI = retrofitFactory.create(client, homeServerUrl) val authAPI = retrofitFactory.create(client, homeServerUrl)
.create(AuthAPI::class.java) .create(AuthAPI::class.java)

View file

@ -253,7 +253,7 @@ internal object CertUtil {
val list = ArrayList<ConnectionSpec>() val list = ArrayList<ConnectionSpec>()
list.add(builder.build()) list.add(builder.build())
// TODO: we should display a warning if user enter an http url // TODO: we should display a warning if user enter an http url
if (hsConfig.allowHttpExtension || hsConfig.homeServerUri.toString().startsWith("http://")) { if (hsConfig.allowHttpExtension || hsConfig.homeServerUriBase.toString().startsWith("http://")) {
list.add(ConnectionSpec.CLEARTEXT) list.add(ConnectionSpec.CLEARTEXT)
} }
return list return list

View file

@ -313,7 +313,7 @@ internal class DefaultSession @Inject constructor(
override fun getUiaSsoFallbackUrl(authenticationSessionId: String): String { override fun getUiaSsoFallbackUrl(authenticationSessionId: String): String {
val hsBas = sessionParams.homeServerConnectionConfig val hsBas = sessionParams.homeServerConnectionConfig
.homeServerUri .homeServerUriBase
.toString() .toString()
.trim { it == '/' } .trim { it == '/' }
return buildString { return buildString {

View file

@ -261,7 +261,7 @@ internal abstract class SessionModule {
sessionParams: SessionParams, sessionParams: SessionParams,
retrofitFactory: RetrofitFactory): Retrofit { retrofitFactory: RetrofitFactory): Retrofit {
return retrofitFactory return retrofitFactory
.create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUri.toString()) .create(okHttpClient, sessionParams.homeServerConnectionConfig.homeServerUriBase.toString())
} }
@JvmStatic @JvmStatic

View file

@ -26,7 +26,7 @@ private const val MATRIX_CONTENT_URI_SCHEME = "mxc://"
internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver { internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver {
private val baseUrl = homeServerConnectionConfig.homeServerUri.toString().ensureTrailingSlash() private val baseUrl = homeServerConnectionConfig.homeServerUriBase.toString().ensureTrailingSlash()
override val uploadUrl = baseUrl + NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "upload" override val uploadUrl = baseUrl + NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "upload"

View file

@ -26,8 +26,8 @@ import java.net.Socket
internal class HomeServerAvailabilityChecker(val sessionParams: SessionParams) { internal class HomeServerAvailabilityChecker(val sessionParams: SessionParams) {
fun check(): Boolean { fun check(): Boolean {
val host = sessionParams.homeServerConnectionConfig.homeServerUri.host ?: return false val host = sessionParams.homeServerConnectionConfig.homeServerUriBase.host ?: return false
val port = sessionParams.homeServerConnectionConfig.homeServerUri.port.takeIf { it != -1 } ?: 80 val port = sessionParams.homeServerConnectionConfig.homeServerUriBase.port.takeIf { it != -1 } ?: 80
val timeout = 30_000 val timeout = 30_000
try { try {
Socket().use { socket -> Socket().use { socket ->

View file

@ -50,7 +50,7 @@ class UnrecognizedCertificateDialog @Inject constructor(
val userId = activeSessionHolder.getSafeActiveSession()?.myUserId val userId = activeSessionHolder.getSafeActiveSession()?.myUserId
val hsConfig = activeSessionHolder.getSafeActiveSession()?.sessionParams?.homeServerConnectionConfig ?: return val hsConfig = activeSessionHolder.getSafeActiveSession()?.sessionParams?.homeServerConnectionConfig ?: return
internalShow(activity, unrecognizedFingerprint, true, callback, userId, hsConfig.homeServerUri.toString(), hsConfig.allowedFingerprints.isNotEmpty()) internalShow(activity, unrecognizedFingerprint, true, callback, userId, hsConfig.homeServerUriBase.toString(), hsConfig.allowedFingerprints.isNotEmpty())
} }
/** /**

View file

@ -591,7 +591,7 @@ class LoginViewModel @AssistedInject constructor(
homeServerConnectionConfig: HomeServerConnectionConfig?) { homeServerConnectionConfig: HomeServerConnectionConfig?) {
val alteredHomeServerConnectionConfig = homeServerConnectionConfig val alteredHomeServerConnectionConfig = homeServerConnectionConfig
?.copy( ?.copy(
homeServerUri = Uri.parse(wellKnownPrompt.homeServerUrl), homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) } identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
) )
?: HomeServerConnectionConfig( ?: HomeServerConnectionConfig(
@ -772,7 +772,7 @@ class LoginViewModel @AssistedInject constructor(
data ?: return@launch data ?: return@launch
// Valid Homeserver, add it to the history. // Valid Homeserver, add it to the history.
// Note: we add what the user has input, data.homeServerUrl can be different // Note: we add what the user has input, data.homeServerUrlBase can be different
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString()) rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
val loginMode = when { val loginMode = when {

View file

@ -612,7 +612,7 @@ class LoginViewModel2 @AssistedInject constructor(
homeServerConnectionConfig: HomeServerConnectionConfig?) { homeServerConnectionConfig: HomeServerConnectionConfig?) {
val alteredHomeServerConnectionConfig = homeServerConnectionConfig val alteredHomeServerConnectionConfig = homeServerConnectionConfig
?.copy( ?.copy(
homeServerUri = Uri.parse(wellKnownPrompt.homeServerUrl), homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) } identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
) )
?: HomeServerConnectionConfig( ?: HomeServerConnectionConfig(
@ -752,7 +752,7 @@ class LoginViewModel2 @AssistedInject constructor(
} ?: return@launch } ?: return@launch
// Valid Homeserver, add it to the history. // Valid Homeserver, add it to the history.
// Note: we add what the user has input, data.homeServerUrl can be different // Note: we add what the user has input, data.homeServerUrlBase can be different
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString()) rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
val loginMode = when { val loginMode = when {