Tentative self signed ssl support
This commit is contained in:
parent
9057ee0052
commit
cee82e2a3d
@ -29,13 +29,13 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("io.ktor:ktor-client-core:2.1.1")
|
implementation("io.ktor:ktor-client-core:2.2.4")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:2.1.1")
|
implementation("io.ktor:ktor-client-content-negotiation:2.2.4")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.4")
|
||||||
implementation("io.ktor:ktor-client-logging:2.1.1")
|
implementation("io.ktor:ktor-client-logging:2.2.4")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||||
implementation("io.ktor:ktor-client-auth:2.1.1")
|
implementation("io.ktor:ktor-client-auth:2.2.4")
|
||||||
implementation("org.jsoup:jsoup:1.14.3")
|
implementation("org.jsoup:jsoup:1.15.4")
|
||||||
|
|
||||||
//Dependency Injection
|
//Dependency Injection
|
||||||
implementation("org.kodein.di:kodein-di:7.12.0")
|
implementation("org.kodein.di:kodein-di:7.12.0")
|
||||||
@ -58,7 +58,8 @@ kotlin {
|
|||||||
}
|
}
|
||||||
val androidMain by getting {
|
val androidMain by getting {
|
||||||
dependencies {
|
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")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
|
|
||||||
// Sql
|
// Sql
|
||||||
|
@ -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<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) {
|
||||||
|
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
|
||||||
|
}
|
@ -4,80 +4,23 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
|||||||
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
||||||
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import io.github.aakira.napier.Napier
|
|
||||||
import io.ktor.client.*
|
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.request.*
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
|
||||||
import io.ktor.util.*
|
expect fun createHttpClient(appSettingsService: AppSettingsService, api: SelfossApi): HttpClient
|
||||||
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
|
|
||||||
|
|
||||||
class SelfossApi(private val appSettingsService: AppSettingsService) {
|
class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||||
|
|
||||||
var client = createHttpClient()
|
var client = createHttpClient(appSettingsService, this)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
fun url(path: String) =
|
fun url(path: String) =
|
||||||
"${appSettingsService.getBaseUrl()}$path"
|
"${appSettingsService.getBaseUrl()}$path"
|
||||||
|
|
||||||
fun refreshLoginInformation() {
|
fun refreshLoginInformation() {
|
||||||
appSettingsService.refreshApiSettings()
|
appSettingsService.refreshApiSettings()
|
||||||
client = createHttpClient()
|
client = createHttpClient(appSettingsService, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun constructBasicAuthValue(credentials: BasicAuthCredentials): String {
|
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
|
// Api version was introduces after the POST login, so when there is a version, it should be available
|
||||||
private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
|
fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
|
||||||
private fun hasLoginInfo() =
|
fun hasLoginInfo() =
|
||||||
appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
|
appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user