diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a8369170e..724cc93e3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,6 +28,8 @@ android { vectorDrawables { useSupportLibrary = true } + + buildConfigField("String", "RECAPTCHA_API_SITE_KEY", "\"6LfaxOgpAAAAAI3Sj4rtB2oAFjkRJILiGEt-LUsc\"") } buildTypes { @@ -116,6 +118,7 @@ dependencies { // Others implementation(libs.kotlinx.serialization) implementation(libs.androidx.core.splashscreen) + implementation(libs.recaptcha) // Test testImplementation(libs.junit) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/di/ApplicationModule.kt b/app/src/main/java/com/infomaniak/swisstransfer/di/ApplicationModule.kt index 5c867609e..2490e45d8 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/di/ApplicationModule.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/di/ApplicationModule.kt @@ -17,7 +17,9 @@ */ package com.infomaniak.swisstransfer.di +import android.app.Application import com.infomaniak.multiplatform_swisstransfer.SwissTransferInjection +import com.infomaniak.swisstransfer.ui.MainApplication import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -30,6 +32,9 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object ApplicationModule { + @Provides + fun providesMainApplication(application: Application) = application as MainApplication + @Provides @Singleton fun providesSwissTransferInjection() = SwissTransferInjection() diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt index f5bfb2e38..b86323a53 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt @@ -21,6 +21,7 @@ import android.app.Application import com.infomaniak.multiplatform_swisstransfer.SwissTransferInjection import com.infomaniak.swisstransfer.BuildConfig import com.infomaniak.swisstransfer.ui.utils.AccountUtils +import com.infomaniak.swisstransfer.ui.utils.UploadRecaptcha import dagger.hilt.android.HiltAndroidApp import io.sentry.SentryEvent import io.sentry.SentryOptions @@ -36,13 +37,19 @@ class MainApplication : Application() { @Inject lateinit var swissTransferInjection: SwissTransferInjection + @Inject + lateinit var uploadRecaptcha: UploadRecaptcha + @Inject lateinit var globalCoroutineScope: CoroutineScope override fun onCreate() { super.onCreate() - configureAccountUtils() + globalCoroutineScope.launch { + AccountUtils.init(swissTransferInjection) + uploadRecaptcha.initializeClient() + } SentryAndroid.init(this) { options: SentryAndroidOptions -> // Register the callback as an option @@ -52,8 +59,4 @@ class MainApplication : Application() { } } } - - private fun configureAccountUtils() { - globalCoroutineScope.launch { AccountUtils.init(swissTransferInjection) } - } } diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/utils/UploadRecaptcha.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/utils/UploadRecaptcha.kt new file mode 100644 index 000000000..c8ecdcc43 --- /dev/null +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/utils/UploadRecaptcha.kt @@ -0,0 +1,73 @@ +/* + * 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.ui.utils + +import android.app.Application +import android.util.Log +import com.google.android.recaptcha.Recaptcha +import com.google.android.recaptcha.RecaptchaAction +import com.google.android.recaptcha.RecaptchaClient +import com.infomaniak.swisstransfer.BuildConfig +import com.infomaniak.swisstransfer.ui.MainApplication +import javax.inject.Inject + +/** + * A class responsible for handling reCAPTCHA verification for uploads. + * + * This class uses the Google reCAPTCHA API to verify user actions and prevent abuse. + * It initializes a reCAPTCHA client and provides a method to fetch a reCAPTCHA code. + * + * @property application The application context. + */ +class UploadRecaptcha @Inject constructor(private val application: MainApplication) { + + private var client: RecaptchaClient? = null + + /** + * Initializes the reCAPTCHA client. + * + * This method should be called before attempting to fetch a reCAPTCHA code. + * It fetches the reCAPTCHA client using the provided API site key. + * If an error occurs during initialization, it logs the error message. + * It MUST be called in the MainApplication. + */ + suspend fun initializeClient() { + runCatching { + client = Recaptcha.fetchClient(application, BuildConfig.RECAPTCHA_API_SITE_KEY) + }.onFailure { + Log.e("Recaptcha", "Getting Recaptcha client failed with an exception", it) + } + } + + /** + * Fetches a reCAPTCHA code. + * + * This method executes the reCAPTCHA challenge and retrieves the code. + * The code is then passed to the provided callback function. + * + * @param callback A function that receives the reCAPTCHA code as a string. + * The code may be null if an error occurred. + */ + suspend fun fetchCode(callback: (String?) -> Unit) { + callback(client?.execute(RecaptchaAction.LOGIN)?.getOrNull()) + } + + companion object { + private const val TAG = "UploadRecaptcha" + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 63238dcee..590bd9b93 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ serialization = "1.7.1" swisstransfer = "0.3.0" sentry = "4.12.0" sentry-android = "7.15.0" +recaptcha = "18.6.1" [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } @@ -45,6 +46,7 @@ kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization- navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } swisstransfer-core = { module = "com.github.Infomaniak.multiplatform-SwissTransfer:STCore", version.ref = "swisstransfer" } sentry-android = { module = "io.sentry:sentry-android", version.ref = "sentry-android" } +recaptcha = { module = "com.google.android.recaptcha:recaptcha", version.ref = "recaptcha" } # Tests androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } junit = { group = "junit", name = "junit", version.ref = "junit" }