Fix: Prevent AuthManager state pollution on connection verification (#348)
* Fix: Prevent AuthManager state pollution on connection verification Modified `ConnectViewModel.connect()` to construct a temporary OkHttpClient and Retrofit builder, isolated from the global `ApiClient` state. The connection verification is executed using this temporary service. Only upon a successful HTTP response does the system persist the configured `host`, `port`, and `token` to the global `AuthManager` and triggers an `ApiClient.rebuild()`. This resolves the issue where a failed connection attempt would overwrite active and verified profiles. Extracted `createTempService` factory to `ApiClient` for robustness and clean tests. Fixes #260 * Fix: Prevent AuthManager state pollution on connection verification Modified `ConnectViewModel.connect()` to construct a temporary OkHttpClient and Retrofit builder, isolated from the global `ApiClient` state. The connection verification is executed using this temporary service. Only upon a successful HTTP response does the system persist the configured `host`, `port`, and `token` to the global `AuthManager` and triggers an `ApiClient.rebuild()`. This resolves the issue where a failed connection attempt would overwrite active and verified profiles. Extracted `createTempService` factory to `ApiClient` for robustness and clean tests. Fixes #260 --------- Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
c21e3e9ed0
commit
2c6e437a5b
42 files changed
+296
-667
No files matched your search
@@ -2,7 +2,6 @@ package com.m57.hermescontrol.data.remote
|
||||
|
||||
import com.m57.hermescontrol.BuildConfig
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import okhttp3.CertificatePinner
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
@@ -24,8 +23,6 @@ object ApiClient {
|
||||
@Volatile
|
||||
private var service: HermesApiService? = null
|
||||
|
||||
private val connectionPool = okhttp3.ConnectionPool()
|
||||
|
||||
/** The current [HermesApiService] instance. Lazily created on first access. */
|
||||
val hermesApi: HermesApiService
|
||||
get() {
|
||||
@@ -42,6 +39,47 @@ object ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a standalone, temporary [HermesApiService] without modifying the global instance. */
|
||||
fun createTempService(
|
||||
host: String,
|
||||
port: Int,
|
||||
token: String,
|
||||
): HermesApiService {
|
||||
val tempAuthInterceptor =
|
||||
Interceptor { chain ->
|
||||
val request =
|
||||
if (token.isNotBlank()) {
|
||||
chain
|
||||
.request()
|
||||
.newBuilder()
|
||||
.addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
} else {
|
||||
chain.request()
|
||||
}
|
||||
chain.proceed(request)
|
||||
}
|
||||
|
||||
val tempOkHttp =
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
.addInterceptor(tempAuthInterceptor)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
val tempRetrofit =
|
||||
Retrofit
|
||||
.Builder()
|
||||
.baseUrl("http://$host:$port/")
|
||||
.client(tempOkHttp)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
|
||||
return tempRetrofit.create(HermesApiService::class.java)
|
||||
}
|
||||
|
||||
// ── Internal ─────────────────────────────────────────────────────────
|
||||
|
||||
private fun buildService(): HermesApiService {
|
||||
@@ -75,18 +113,11 @@ object ApiClient {
|
||||
chain.proceed(request)
|
||||
}
|
||||
|
||||
// SEC-11: No certificate pinning is configured by default since the app
|
||||
// intentionally uses HTTP for LAN and hosts are dynamic.
|
||||
// CertificatePinner infrastructure is provided here if HTTPS is used with a known host.
|
||||
val certificatePinner = CertificatePinner.Builder().build()
|
||||
|
||||
val okHttp =
|
||||
OkHttpClient
|
||||
.Builder()
|
||||
.connectionPool(connectionPool)
|
||||
.addInterceptor(authInterceptor)
|
||||
.addInterceptor(logging)
|
||||
.certificatePinner(certificatePinner)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
|
||||
Reference in new issue
Block a user