From a546101aeda6dada3bc8a902eefd016df1e715f6 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 26 Dec 2024 10:35:11 +0100 Subject: [PATCH 1/5] chore: Add UserAgent qualifiers --- .../swisstransfer/di/ClientQualifiers.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/src/main/java/com/infomaniak/swisstransfer/di/ClientQualifiers.kt diff --git a/app/src/main/java/com/infomaniak/swisstransfer/di/ClientQualifiers.kt b/app/src/main/java/com/infomaniak/swisstransfer/di/ClientQualifiers.kt new file mode 100644 index 000000000..675040b23 --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/di/ClientQualifiers.kt @@ -0,0 +1,24 @@ +/* + * Infomaniak SwissTransfer - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.swisstransfer.di + +import javax.inject.Qualifier + +@Retention(AnnotationRetention.BINARY) +@Qualifier +annotation class UserAgent From 4607ed27075e4f9e5c43ba642c447b08fb850d82 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 26 Dec 2024 10:37:59 +0100 Subject: [PATCH 2/5] chore: Add support for custom userAgent --- .../infomaniak/core2/appintegrity/ApiClientProvider.kt | 10 ++++++++-- .../core2/appintegrity/AppIntegrityManager.kt | 4 ++-- .../core2/appintegrity/AppIntegrityRepository.kt | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/ApiClientProvider.kt b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/ApiClientProvider.kt index 85489dbe5..98b885732 100644 --- a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/ApiClientProvider.kt +++ b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/ApiClientProvider.kt @@ -29,6 +29,7 @@ import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.plugins.HttpRequestRetry import io.ktor.client.plugins.HttpResponseValidator import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.UserAgent import io.ktor.client.plugins.compression.ContentEncoding import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.statement.HttpResponse @@ -37,7 +38,10 @@ import io.ktor.serialization.kotlinx.json.json import kotlinx.io.IOException import kotlinx.serialization.json.Json -internal class ApiClientProvider(engine: HttpClientEngine = OkHttp.create()) { +internal class ApiClientProvider( + engine: HttpClientEngine = OkHttp.create(), + private val userAgent: String, +) { val json = Json { ignoreUnknownKeys = true @@ -50,7 +54,9 @@ internal class ApiClientProvider(engine: HttpClientEngine = OkHttp.create()) { private fun createHttpClient(engine: HttpClientEngine): HttpClient { val block: HttpClientConfig<*>.() -> Unit = { - expectSuccess = true + install(UserAgent) { + agent = userAgent + } install(ContentNegotiation) { json(this@ApiClientProvider.json) } diff --git a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityManager.kt b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityManager.kt index 518fdc235..ae3ce88e8 100644 --- a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityManager.kt +++ b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityManager.kt @@ -36,11 +36,11 @@ import java.util.UUID * - the standard request ([requestIntegrityVerdictToken]) that need a warm-up first ([warmUpTokenProvider]) * - the classic request ([requestClassicIntegrityVerdictToken]) that need additional API checks */ -class AppIntegrityManager(private val appContext: Context) { +class AppIntegrityManager(private val appContext: Context, userAgent: String) { private var appIntegrityTokenProvider: StandardIntegrityTokenProvider? = null private val classicIntegrityTokenProvider by lazy { IntegrityManagerFactory.create(appContext) } - private val appIntegrityRepository by lazy { AppIntegrityRepository() } + private val appIntegrityRepository by lazy { AppIntegrityRepository(userAgent) } private var challenge = "" private var challengeId = "" diff --git a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityRepository.kt b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityRepository.kt index 4deddcb94..1085e7d30 100644 --- a/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityRepository.kt +++ b/Core2/AppIntegrity/src/main/java/com/infomaniak/core2/appintegrity/AppIntegrityRepository.kt @@ -33,9 +33,9 @@ import io.ktor.http.HeadersBuilder import io.ktor.http.Url import io.ktor.http.contentType -internal class AppIntegrityRepository { +internal class AppIntegrityRepository(userAgent: String) { - private val apiClientProvider by lazy { ApiClientProvider() } + private val apiClientProvider by lazy { ApiClientProvider(userAgent = userAgent) } suspend fun getChallenge(challengeId: String): ApiResponse { val body = mapOf("challenge_id" to challengeId) From 206b05497f4a0bd01a253201b0e04856ca6a41e9 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 26 Dec 2024 10:38:25 +0100 Subject: [PATCH 3/5] chore: Update ApiClientProviderTest --- .../com/infomaniak/core2/appintegrity/ApiClientProviderTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core2/AppIntegrity/src/test/java/com/infomaniak/core2/appintegrity/ApiClientProviderTest.kt b/Core2/AppIntegrity/src/test/java/com/infomaniak/core2/appintegrity/ApiClientProviderTest.kt index 000ccfa2c..56b803444 100644 --- a/Core2/AppIntegrity/src/test/java/com/infomaniak/core2/appintegrity/ApiClientProviderTest.kt +++ b/Core2/AppIntegrity/src/test/java/com/infomaniak/core2/appintegrity/ApiClientProviderTest.kt @@ -23,7 +23,8 @@ class ApiClientProviderTest { status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/json"), ) - } + }, + userAgent = "Ktor client test" ) } From b7e884ea24479e0c8584407dfa2330382325cafe Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 26 Dec 2024 10:45:44 +0100 Subject: [PATCH 4/5] chore: Inject UserAgent --- .../di/SwissTransferInjectionModule.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/di/SwissTransferInjectionModule.kt b/app/src/main/java/com/infomaniak/swisstransfer/di/SwissTransferInjectionModule.kt index 4b97f730e..724fe654d 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/di/SwissTransferInjectionModule.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/di/SwissTransferInjectionModule.kt @@ -33,14 +33,20 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object SwissTransferInjectionModule { + @UserAgent @Provides @Singleton - fun providesSwissTransferInjection(): SwissTransferInjection { - val userAgent = buildUserAgent( + fun providesUserAgent(): String { + return buildUserAgent( appId = BuildConfig.APPLICATION_ID, appVersionCode = BuildConfig.VERSION_CODE, appVersionName = BuildConfig.VERSION_NAME, ) + } + + @Provides + @Singleton + fun providesSwissTransferInjection(@UserAgent userAgent: String): SwissTransferInjection { return SwissTransferInjection(environment = ApiEnvironment.Prod, userAgent = userAgent) } @@ -66,5 +72,7 @@ object SwissTransferInjectionModule { @Provides @Singleton - fun providesAppIntegrityManger(application: Application) = AppIntegrityManager(application) + fun providesAppIntegrityManger(application: Application, @UserAgent userAgent: String): AppIntegrityManager { + return AppIntegrityManager(application, userAgent) + } } From ccd7d8002115d42df4f707bd569409ee1314cae9 Mon Sep 17 00:00:00 2001 From: Abdourahamane Boinaidi Date: Thu, 26 Dec 2024 10:50:42 +0100 Subject: [PATCH 5/5] chore: Inject userAgent if needed --- .../main/transferdetails/TransferDetailsViewModel.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt index f5dd9e05a..ad0ec6a1e 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDetailsViewModel.kt @@ -23,13 +23,12 @@ import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.infomaniak.core2.DownloadManagerUtils -import com.infomaniak.core2.buildUserAgent import com.infomaniak.multiplatform_swisstransfer.SharedApiUrlCreator import com.infomaniak.multiplatform_swisstransfer.common.interfaces.ui.TransferUi import com.infomaniak.multiplatform_swisstransfer.common.models.TransferDirection import com.infomaniak.multiplatform_swisstransfer.managers.TransferManager import com.infomaniak.sentry.SentryLog -import com.infomaniak.swisstransfer.BuildConfig +import com.infomaniak.swisstransfer.di.UserAgent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* @@ -40,6 +39,7 @@ import javax.inject.Inject class TransferDetailsViewModel @Inject constructor( private val transferManager: TransferManager, private val sharedApiUrlCreator: SharedApiUrlCreator, + @UserAgent private val userAgent: String, ) : ViewModel() { private val _transferUuidFlow = MutableSharedFlow(1) @@ -75,11 +75,6 @@ class TransferDetailsViewModel @Inject constructor( fun startDownloadingAllFiles(transfer: TransferUi) { viewModelScope.launch { val url = sharedApiUrlCreator.downloadFilesUrl(transfer.uuid) ?: return@launch - val userAgent = buildUserAgent( - appId = BuildConfig.APPLICATION_ID, - appVersionCode = BuildConfig.VERSION_CODE, - appVersionName = BuildConfig.VERSION_NAME, - ) when (transfer.files.size) { 1 -> {