From ed06b22a7733b7d889b9271437aa8311506d9572 Mon Sep 17 00:00:00 2001 From: davidoskky Date: Sat, 15 Apr 2023 18:49:46 +0200 Subject: [PATCH] Tentative self signed ssl support --- shared/build.gradle.kts | 15 ++-- .../readerforselfossv2/rest/SelfossApi.kt | 82 +++++++++++++++++++ .../readerforselfossv2/rest/SelfossApi.kt | 69 ++-------------- 3 files changed, 96 insertions(+), 70 deletions(-) create mode 100644 shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 8408522..747592b 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -29,13 +29,13 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("io.ktor:ktor-client-core:2.1.1") - implementation("io.ktor:ktor-client-content-negotiation:2.1.1") - implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1") - implementation("io.ktor:ktor-client-logging:2.1.1") + implementation("io.ktor:ktor-client-core:2.2.4") + implementation("io.ktor:ktor-client-content-negotiation:2.2.4") + implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.4") + implementation("io.ktor:ktor-client-logging:2.2.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") - implementation("io.ktor:ktor-client-auth:2.1.1") - implementation("org.jsoup:jsoup:1.14.3") + implementation("io.ktor:ktor-client-auth:2.2.4") + implementation("org.jsoup:jsoup:1.15.4") //Dependency Injection implementation("org.kodein.di:kodein-di:7.12.0") @@ -58,7 +58,8 @@ kotlin { } val androidMain by getting { dependencies { - implementation("io.ktor:ktor-client-okhttp:2.1.1") + implementation("com.squareup.okhttp3:okhttp:4.10.0") + implementation("io.ktor:ktor-client-okhttp:2.2.4") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // Sql diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt new file mode 100644 index 0000000..80c9b08 --- /dev/null +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -0,0 +1,82 @@ +package bou.amine.apps.readerforselfossv2.rest + +import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import io.github.aakira.napier.Napier +import io.ktor.client.* +import io.ktor.client.engine.okhttp.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.cache.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.cookies.* +import io.ktor.client.plugins.logging.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json +import okhttp3.OkHttpClient +import org.apache.http.conn.ssl.AllowAllHostnameVerifier +import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext +import javax.net.ssl.X509TrustManager + +class NaiveTrustManager : X509TrustManager { + override fun checkClientTrusted(chain: Array?, authType: String?) {} + + override fun checkServerTrusted(chain: Array?, authType: String?) {} + + override fun getAcceptedIssuers(): Array = arrayOf() +} + +actual fun createHttpClient(appSettingsService: AppSettingsService, api: SelfossApi) = + HttpClient(OkHttp) { + engine { + val trustManager = NaiveTrustManager() + val sslContext = SSLContext.getInstance("TLS").apply { + init(null, arrayOf(trustManager), null) + } + preconfigured = OkHttpClient().newBuilder() + .sslSocketFactory( + sslSocketFactory = sslContext.socketFactory, + trustManager = trustManager + ) + .hostnameVerifier(AllowAllHostnameVerifier()) + .build() + } + install(ContentNegotiation) { + install(HttpCache) + json(Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + }) + } + install(Logging) { + logger = object : Logger { + override fun log(message: String) { + Napier.d(message, tag = "LogApiCalls") + } + } + level = LogLevel.INFO + } + install(HttpTimeout) { + requestTimeoutMillis = appSettingsService.getApiTimeout() + } + install(HttpCookies) + install(HttpRequestRetry) { + maxRetries = 2 + retryIf { _, response -> + response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo() + } + modifyRequest { + Napier.i("Will modify", tag = "HttpSend") + CoroutineScope(Dispatchers.Main).launch { + Napier.i("Will login", tag = "HttpSend") + api.login() + Napier.i("Did login", tag = "HttpSend") + } + } + } + expectSuccess = false + } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index b97bd93..924ef12 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -4,80 +4,23 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.StatusAndData import bou.amine.apps.readerforselfossv2.model.SuccessResponse import bou.amine.apps.readerforselfossv2.service.AppSettingsService -import io.github.aakira.napier.Napier import io.ktor.client.* -import io.ktor.client.plugins.* -import io.ktor.client.plugins.auth.providers.* -import io.ktor.client.plugins.cache.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.plugins.cookies.* -import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* -import io.ktor.util.* -import io.ktor.utils.io.charsets.* -import io.ktor.utils.io.core.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.serialization.json.Json + +expect fun createHttpClient(appSettingsService: AppSettingsService, api: SelfossApi): HttpClient class SelfossApi(private val appSettingsService: AppSettingsService) { - var client = createHttpClient() - - private fun createHttpClient(): HttpClient { - val client = HttpClient { - install(ContentNegotiation) { - install(HttpCache) - json(Json { - prettyPrint = true - isLenient = true - ignoreUnknownKeys = true - explicitNulls = false - }) - } - install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.d(message, tag = "LogApiCalls") - } - } - level = LogLevel.INFO - } - install(HttpTimeout) { - requestTimeoutMillis = appSettingsService.getApiTimeout() - } - install(HttpCookies) - install(HttpRequestRetry) { - maxRetries = 2 - retryIf { _, response -> - response.status == HttpStatusCode.Forbidden && shouldHavePostLogin() && hasLoginInfo() - } - modifyRequest { - Napier.i("Will modify", tag = "HttpSend") - CoroutineScope(Dispatchers.Main).launch { - Napier.i("Will login", tag = "HttpSend") - this@SelfossApi.login() - Napier.i("Did login", tag = "HttpSend") - } - } - } - expectSuccess = false - } - - - return client - } + var client = createHttpClient(appSettingsService, this) fun url(path: String) = "${appSettingsService.getBaseUrl()}$path" fun refreshLoginInformation() { appSettingsService.refreshApiSettings() - client = createHttpClient() + client = createHttpClient(appSettingsService, this) } fun constructBasicAuthValue(credentials: BasicAuthCredentials): String { @@ -88,8 +31,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { } // Api version was introduces after the POST login, so when there is a version, it should be available - private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1 - private fun hasLoginInfo() = + fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1 + fun hasLoginInfo() = appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword() .isNotEmpty()