Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Ensure that the app integrity uses the same userAgent as the app client #287

Merged
merged 5 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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([email protected])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
val body = mapOf("challenge_id" to challengeId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class ApiClientProviderTest {
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json"),
)
}
},
userAgent = "Ktor client test"
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.swisstransfer.di

import javax.inject.Qualifier

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class UserAgent
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand All @@ -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<String>(1)
Expand Down Expand Up @@ -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 -> {
Expand Down
Loading