diff --git a/CHANGES.md b/CHANGES.md index c5ad3cd1fa..fd529f9a27 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Improvements 🙌: - Give user the possibility to prevent accidental call (#1869) - Display device information (name, id and key) in Cryptography setting screen (#1784) - Ensure users do not accidentally ignore other users (#1890) + - Support new config.json format and config.domain.json files (#1682) - Increase Font size on Calling screen (#1643) Bugfix 🐛: diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt index 00eb7e8599..d0c02b6027 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt @@ -42,6 +42,11 @@ import retrofit2.http.Url * The login REST API. */ internal interface AuthAPI { + /** + * Get a Riot config file, using the name including the domain + */ + @GET("config.{domain}.json") + fun getRiotConfigDomain(@Path("domain") domain: String): Call /** * Get a Riot config file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 1294855b6e..02c48dab07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -19,6 +19,9 @@ package org.matrix.android.sdk.internal.auth import android.net.Uri import dagger.Lazy +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.Credentials @@ -50,12 +53,8 @@ import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.task.launchToCallback import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import org.matrix.android.sdk.internal.util.exhaustive import org.matrix.android.sdk.internal.util.toCancelable import org.matrix.android.sdk.internal.wellknown.GetWellknownTask -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient import javax.inject.Inject import javax.net.ssl.HttpsURLConnection @@ -157,7 +156,7 @@ internal class DefaultAuthenticationService @Inject constructor( if (it is Failure.OtherServerError && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { // It's maybe a Riot url? - getRiotLoginFlowInternal(homeServerConnectionConfig) + getRiotDomainLoginFlowInternal(homeServerConnectionConfig) } else { throw it } @@ -166,6 +165,37 @@ internal class DefaultAuthenticationService @Inject constructor( } } + private suspend fun getRiotDomainLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { + val authAPI = buildAuthAPI(homeServerConnectionConfig) + + val domain = homeServerConnectionConfig.homeServerUri.host + ?: return getRiotLoginFlowInternal(homeServerConnectionConfig) + + // Ok, try to get the config.domain.json file of a RiotWeb client + return runCatching { + executeRequest(null) { + apiCall = authAPI.getRiotConfigDomain(domain) + } + } + .map { riotConfig -> + onRiotConfigRetrieved(homeServerConnectionConfig, riotConfig) + } + .fold( + { + it + }, + { + if (it is Failure.OtherServerError + && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + // Try with config.json + getRiotLoginFlowInternal(homeServerConnectionConfig) + } else { + throw it + } + } + ) + } + private suspend fun getRiotLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { val authAPI = buildAuthAPI(homeServerConnectionConfig) @@ -176,23 +206,7 @@ internal class DefaultAuthenticationService @Inject constructor( } } .map { riotConfig -> - if (riotConfig.defaultHomeServerUrl?.isNotBlank() == true) { - // Ok, good sign, we got a default hs url - val newHomeServerConnectionConfig = homeServerConnectionConfig.copy( - homeServerUri = Uri.parse(riotConfig.defaultHomeServerUrl) - ) - - val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig) - - val versions = executeRequest(null) { - apiCall = newAuthAPI.versions() - } - - getLoginFlowResult(newAuthAPI, versions, riotConfig.defaultHomeServerUrl) - } else { - // Config exists, but there is no default homeserver url (ex: https://riot.im/app) - throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) - } + onRiotConfigRetrieved(homeServerConnectionConfig, riotConfig) } .fold( { @@ -210,6 +224,27 @@ internal class DefaultAuthenticationService @Inject constructor( ) } + private suspend fun onRiotConfigRetrieved(homeServerConnectionConfig: HomeServerConnectionConfig, riotConfig: RiotConfig): LoginFlowResult { + val defaultHomeServerUrl = riotConfig.getPreferredHomeServerUrl() + if (defaultHomeServerUrl?.isNotEmpty() == true) { + // Ok, good sign, we got a default hs url + val newHomeServerConnectionConfig = homeServerConnectionConfig.copy( + homeServerUri = Uri.parse(defaultHomeServerUrl) + ) + + val newAuthAPI = buildAuthAPI(newHomeServerConnectionConfig) + + val versions = executeRequest(null) { + apiCall = newAuthAPI.versions() + } + + return getLoginFlowResult(newAuthAPI, versions, defaultHomeServerUrl) + } else { + // Config exists, but there is no default homeserver url (ex: https://riot.im/app) + throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) + } + } + private suspend fun getWellknownLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig): LoginFlowResult { val domain = homeServerConnectionConfig.homeServerUri.host ?: throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) @@ -234,7 +269,7 @@ internal class DefaultAuthenticationService @Inject constructor( getLoginFlowResult(newAuthAPI, versions, wellknownResult.homeServerUrl) } else -> throw Failure.OtherServerError("", HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) - }.exhaustive + } } private suspend fun getLoginFlowResult(authAPI: AuthAPI, versions: Versions, homeServerUrl: String): LoginFlowResult { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/RiotConfig.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/RiotConfig.kt index 42db315262..9fb8963812 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/RiotConfig.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/RiotConfig.kt @@ -21,9 +21,31 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class RiotConfig( - // There are plenty of other elements in the file config.json of a RiotWeb client, but for the moment only one is interesting - // Ex: "brand", "branding", etc. +internal data class RiotConfig( + /** + * This is now deprecated, but still used first, rather than value from "default_server_config" + */ @Json(name = "default_hs_url") - val defaultHomeServerUrl: String? + val defaultHomeServerUrl: String?, + + @Json(name = "default_server_config") + val defaultServerConfig: RiotConfigDefaultServerConfig? +) { + fun getPreferredHomeServerUrl(): String? { + return defaultHomeServerUrl + ?.takeIf { it.isNotEmpty() } + ?: defaultServerConfig?.homeServer?.baseURL + } +} + +@JsonClass(generateAdapter = true) +internal data class RiotConfigDefaultServerConfig( + @Json(name = "m.homeserver") + val homeServer: RiotConfigBaseConfig? = null +) + +@JsonClass(generateAdapter = true) +internal data class RiotConfigBaseConfig( + @Json(name = "base_url") + val baseURL: String? = null )