Tentative self signed ssl support #141

Merged
AmineB merged 12 commits from davidoskky/ReaderForSelfoss-multiplatform:self_ssl into master 2023-09-17 18:28:48 +00:00
5 changed files with 90 additions and 97 deletions
Showing only changes of commit 70ad5f322c - Show all commits

View File

@ -144,6 +144,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
repository.refreshLoginInformation(url, login, password)
CoroutineScope(Dispatchers.Main).launch {
repository.updateApiInformation()
AmineB marked this conversation as resolved Outdated

repository.updateApiInformation() is called in goToMain which is called at line 152. This should not be needed.

`repository.updateApiInformation()` is called in `goToMain` which is called at line 152. This should not be needed.

Updating the api information is required for the following login call. This can probably be fixed by restructuring the code of the login page.

Updating the api information is required for the following login call. This can probably be fixed by restructuring the code of the login page.
val result = repository.login()
if (result) {
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()

View File

@ -29,12 +29,16 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.ktor:ktor-client-core:2.2.4")

Please revert this

Please revert this

Do you mean the whole version upgrade?

Do you mean the whole version upgrade?

Yes please

Yes please
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")
val ktorVersion = "2.3.2"
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("io.ktor:ktor-client-auth:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("io.ktor:ktor-client-auth:2.2.4")
implementation("org.jsoup:jsoup:1.15.4")
//Dependency Injection

View File

@ -0,0 +1,17 @@
package bou.amine.apps.readerforselfossv2.rest
import io.ktor.client.engine.cio.CIOEngineConfig
import java.security.cert.X509Certificate
import javax.net.ssl.X509TrustManager
class NaiveTrustManager : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
}
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
config.https.trustManager = NaiveTrustManager()
}

View File

@ -1,90 +0,0 @@
package bou.amine.apps.readerforselfossv2.rest
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import io.github.aakira.napier.Napier
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.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<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
}
actual fun createHttpClient(
appSettingsService: AppSettingsService,
api: SelfossApi
) =
HttpClient(OkHttp) {
if (appSettingsService.getSelfSigned()) {
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
}

View File

@ -4,22 +4,83 @@ 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.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.engine.cio.CIOEngineConfig
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.auth.providers.BasicAuthCredentials
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.parameter
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.serialization.kotlinx.json.json
import io.ktor.util.encodeBase64
import io.ktor.utils.io.charsets.Charsets
import io.ktor.utils.io.core.toByteArray
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
expect fun createHttpClient(
expect fun setupInsecureHTTPEngine(config: CIOEngineConfig)
fun createHttpClient(
appSettingsService: AppSettingsService,
api: SelfossApi

Why is this needed ? Can't it be replaced by this ?

Why is this needed ? Can't it be replaced by `this` ?
): HttpClient
) =
HttpClient(CIO) {
if (appSettingsService.getSelfSigned()) {
engine {
setupInsecureHTTPEngine(this)
}
}
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
}
class SelfossApi(private val appSettingsService: AppSettingsService) {