Skip to content

Commit

Permalink
Gradle fixes for some deprecated functions, Refactor UserAuthenticati…
Browse files Browse the repository at this point in the history
…onController to use BiometricController for basic operations
  • Loading branch information
stzouvaras committed Mar 1, 2024
1 parent 3e9374e commit 7894b3d
Show file tree
Hide file tree
Showing 26 changed files with 132 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ internal fun Project.configureAndroidCompose(
add("testImplementation", libs.findLibrary("robolectric").get())
}

@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// For Robolectric
Expand All @@ -86,7 +87,9 @@ private fun Project.buildComposeMetricsParameters(): List<String> {

val enableMetrics = (enableMetricsProvider.orNull == "true")
if (enableMetrics) {
val metricsFolder = rootProject.buildDir.resolve("compose-metrics").resolve(relativePath)
val metricsFolder =
rootProject.layout.buildDirectory.get().asFile.resolve("compose-metrics")
.resolve(relativePath)
metricParameters.add("-P")
metricParameters.add(
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath
Expand All @@ -96,7 +99,9 @@ private fun Project.buildComposeMetricsParameters(): List<String> {
val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports")
val enableReports = (enableReportsProvider.orNull == "true")
if (enableReports) {
val reportsFolder = rootProject.buildDir.resolve("compose-reports").resolve(relativePath)
val reportsFolder =
rootProject.layout.buildDirectory.get().asFile.resolve("compose-reports")
.resolve(relativePath)
metricParameters.add("-P")
metricParameters.add(
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ import org.gradle.api.Project
*
* Note: this could be improved by checking other potential sourceSets based on buildTypes and flavors.
*/
@Suppress("UnstableApiUsage")
internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests(
project: Project,
) = beforeVariants {
it.enableAndroidTest = it.enableAndroidTest
it.androidTest.enable = it.androidTest.enable
&& project.projectDir.resolve("src/androidTest").exists()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package eu.europa.ec.businesslogic

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,25 @@
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.businesslogic.controller.biometry
package eu.europa.ec.businesslogic.controller.authentication

import android.content.Context
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import eu.europa.ec.businesslogic.controller.biometry.BiometricController
import eu.europa.ec.businesslogic.controller.biometry.BiometricsAvailability
import eu.europa.ec.businesslogic.model.BiometricCrypto
import eu.europa.ec.resourceslogic.R
import eu.europa.ec.resourceslogic.provider.ResourceProvider
import kotlinx.coroutines.launch

interface UserAuthenticationController {
fun deviceSupportsBiometrics(listener: (BiometricsAvailability) -> Unit)
fun authenticate(
context: Context,
biometryCrypto: BiometryCrypto,
biometryCrypto: BiometricCrypto,
userAuthenticationBiometricResult: UserAuthenticationResult
)
}
Expand All @@ -44,48 +47,37 @@ class UserAuthenticationControllerImpl(

override fun authenticate(
context: Context,
biometryCrypto: BiometryCrypto,
biometryCrypto: BiometricCrypto,
userAuthenticationBiometricResult: UserAuthenticationResult
) {
context as FragmentActivity
(context as? FragmentActivity)?.let { activity ->

val prompt = BiometricPrompt(
context,
ContextCompat.getMainExecutor(context),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
userAuthenticationBiometricResult.onAuthenticationError()
}
activity.lifecycleScope.launch {

override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
userAuthenticationBiometricResult.onAuthenticationSuccess()
}
val data = biometricController.authenticate(
activity = activity,
biometryCrypto = biometryCrypto,
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(resourceProvider.getString(R.string.biometric_prompt_title))
.setSubtitle(resourceProvider.getString(R.string.biometric_prompt_subtitle))
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
)

override fun onAuthenticationFailed() {
if (data.authenticationResult != null) {
userAuthenticationBiometricResult.onAuthenticationSuccess()
} else if (data.hasError) {
userAuthenticationBiometricResult.onAuthenticationError()
} else {
userAuthenticationBiometricResult.onAuthenticationFailure()
}
}
)

val builder = BiometricPrompt.PromptInfo.Builder()
.setTitle(resourceProvider.getString(R.string.biometric_prompt_title))
.setSubtitle(resourceProvider.getString(R.string.biometric_prompt_subtitle))
.setAllowedAuthenticators(BIOMETRIC_WEAK or DEVICE_CREDENTIAL)
.build()

biometryCrypto.cryptoObject?.let {
prompt.authenticate(
builder,
it
)
} ?: prompt.authenticate(builder)
}
}
}

data class UserAuthenticationResult(
val onAuthenticationSuccess: () -> Unit = {},
val onAuthenticationError: () -> Unit = {},
val onAuthenticationFailure: () -> Unit = {},
)

data class BiometryCrypto(val cryptoObject: BiometricPrompt.CryptoObject?)
)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import eu.europa.ec.businesslogic.controller.crypto.CryptoController
import eu.europa.ec.businesslogic.controller.storage.PrefKeys
import eu.europa.ec.businesslogic.extension.decodeFromPemBase64String
import eu.europa.ec.businesslogic.extension.encodeToPemBase64String
import eu.europa.ec.businesslogic.model.BiometricCrypto
import eu.europa.ec.businesslogic.model.BiometricData
import eu.europa.ec.resourceslogic.R
import eu.europa.ec.resourceslogic.provider.ResourceProvider
Expand All @@ -50,6 +51,12 @@ enum class BiometricsAuthError(val code: Int) {
interface BiometricController {
fun deviceSupportsBiometrics(listener: (BiometricsAvailability) -> Unit)
fun authenticate(context: Context, listener: (BiometricsAuthenticate) -> Unit)
suspend fun authenticate(
activity: FragmentActivity,
biometryCrypto: BiometricCrypto,
promptInfo: BiometricPrompt.PromptInfo
): BiometricPromptData

fun launchBiometricSystemScreen()
}

Expand Down Expand Up @@ -88,7 +95,15 @@ class BiometricControllerImpl(
return@launch
}

val data = authenticate(activity = activity, cipher = cipher)
val data = authenticate(
activity = activity,
biometryCrypto = BiometricCrypto(BiometricPrompt.CryptoObject(cipher)),
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.biometric_prompt_title))
.setSubtitle(activity.getString(R.string.biometric_prompt_subtitle))
.setNegativeButtonText(activity.getString(R.string.generic_cancel))
.build()
)

if (data.authenticationResult != null) {
val state = verifyCrypto(
Expand Down Expand Up @@ -124,11 +139,12 @@ class BiometricControllerImpl(
resourceProvider.provideContext().startActivity(enrollIntent)
}

private suspend fun authenticate(
override suspend fun authenticate(
activity: FragmentActivity,
cipher: Cipher,
biometryCrypto: BiometricCrypto,
promptInfo: BiometricPrompt.PromptInfo
): BiometricPromptData = suspendCancellableCoroutine { continuation ->
BiometricPrompt(
val prompt = BiometricPrompt(
activity,
ContextCompat.getMainExecutor(activity),
object : BiometricPrompt.AuthenticationCallback() {
Expand All @@ -152,14 +168,14 @@ class BiometricControllerImpl(
}
}
}
).authenticate(
BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.biometric_prompt_title))
.setSubtitle(activity.getString(R.string.biometric_prompt_subtitle))
.setNegativeButtonText(activity.getString(R.string.generic_cancel))
.build(),
BiometricPrompt.CryptoObject(cipher)
)

biometryCrypto.cryptoObject?.let {
prompt.authenticate(
promptInfo,
it
)
} ?: prompt.authenticate(promptInfo)
}

private suspend fun retrieveCrypto(): Pair<BiometricData?, Cipher?> =
Expand Down Expand Up @@ -227,4 +243,6 @@ data class BiometricPromptData(
val authenticationResult: AuthenticationResult?,
val errorCode: Int = -1,
val errorString: CharSequence = "",
)
) {
val hasError: Boolean get() = errorCode != -1
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import eu.europa.ec.businesslogic.controller.log.LogController
import eu.europa.ec.resourceslogic.provider.ResourceProvider
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.*
import java.util.Arrays

enum class AndroidInstaller {
TRUSTED, UNKNOWN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package eu.europa.ec.businesslogic.controller.walletcore

import eu.europa.ec.businesslogic.controller.biometry.BiometryCrypto
import eu.europa.ec.businesslogic.controller.biometry.UserAuthenticationResult
import eu.europa.ec.businesslogic.controller.authentication.UserAuthenticationResult
import eu.europa.ec.businesslogic.extension.safeAsync
import eu.europa.ec.businesslogic.model.BiometricCrypto
import eu.europa.ec.eudi.wallet.EudiWallet
import eu.europa.ec.eudi.wallet.document.DeleteDocumentResult
import eu.europa.ec.eudi.wallet.document.Document
Expand All @@ -43,7 +43,7 @@ sealed class IssueDocumentPartialState {
data class Success(val documentId: String) : IssueDocumentPartialState()
data class Failure(val errorMessage: String) : IssueDocumentPartialState()
data class UserAuthRequired(
val crypto: BiometryCrypto,
val crypto: BiometricCrypto,
val resultHandler: UserAuthenticationResult
) : IssueDocumentPartialState()
}
Expand All @@ -52,7 +52,7 @@ sealed class OpenId4VCIIssueDocumentPartialState {
data class Success(val documentId: String) : OpenId4VCIIssueDocumentPartialState()
data class Failure(val errorMessage: String) : OpenId4VCIIssueDocumentPartialState()
data class UserAuthRequired(
val crypto: BiometryCrypto,
val crypto: BiometricCrypto,
val resultHandler: UserAuthenticationResult
) : OpenId4VCIIssueDocumentPartialState()
}
Expand Down Expand Up @@ -286,7 +286,7 @@ class WalletCoreDocumentsControllerImpl(
is IssueDocumentResult.UserAuthRequired -> {
trySendBlocking(
OpenId4VCIIssueDocumentPartialState.UserAuthRequired(
BiometryCrypto(result.cryptoObject),
BiometricCrypto(result.cryptoObject),
UserAuthenticationResult(
onAuthenticationSuccess = { result.resume() },
onAuthenticationError = { result.cancel() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
package eu.europa.ec.businesslogic.controller.walletcore

import androidx.activity.ComponentActivity
import eu.europa.ec.businesslogic.controller.biometry.BiometryCrypto
import eu.europa.ec.businesslogic.controller.biometry.UserAuthenticationResult
import eu.europa.ec.businesslogic.controller.authentication.UserAuthenticationResult
import eu.europa.ec.businesslogic.di.WalletPresentationScope
import eu.europa.ec.businesslogic.extension.safeAsync
import eu.europa.ec.businesslogic.model.BiometricCrypto
import eu.europa.ec.businesslogic.util.EudiWalletListenerWrapper
import eu.europa.ec.eudi.iso18013.transfer.DisclosedDocuments
import eu.europa.ec.eudi.iso18013.transfer.RequestDocument
Expand Down Expand Up @@ -64,7 +64,7 @@ sealed class TransferEventPartialState {
sealed class SendRequestedDocumentsPartialState {
data class Failure(val error: String) : SendRequestedDocumentsPartialState()
data class UserAuthenticationRequired(
val crypto: BiometryCrypto,
val crypto: BiometricCrypto,
val resultHandler: UserAuthenticationResult
) : SendRequestedDocumentsPartialState()

Expand All @@ -79,7 +79,7 @@ sealed class ResponseReceivedPartialState {

sealed class WalletCorePartialState {
data class UserAuthenticationRequired(
val crypto: BiometryCrypto,
val crypto: BiometricCrypto,
val resultHandler: UserAuthenticationResult
) : WalletCorePartialState()

Expand Down Expand Up @@ -290,7 +290,7 @@ class WalletCorePresentationControllerImpl(
is ResponseResult.UserAuthRequired -> {
emit(
SendRequestedDocumentsPartialState.UserAuthenticationRequired(
BiometryCrypto(response.cryptoObject),
BiometricCrypto(response.cryptoObject),
UserAuthenticationResult(
onAuthenticationSuccess = {
eudiWallet.sendResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import eu.europa.ec.businesslogic.config.ConfigSecurityLogic
import eu.europa.ec.businesslogic.config.ConfigSecurityLogicImpl
import eu.europa.ec.businesslogic.config.WalletCoreConfig
import eu.europa.ec.businesslogic.config.WalletCoreConfigImpl
import eu.europa.ec.businesslogic.controller.authentication.UserAuthenticationController
import eu.europa.ec.businesslogic.controller.authentication.UserAuthenticationControllerImpl
import eu.europa.ec.businesslogic.controller.biometry.BiometricController
import eu.europa.ec.businesslogic.controller.biometry.BiometricControllerImpl
import eu.europa.ec.businesslogic.controller.biometry.UserAuthenticationController
import eu.europa.ec.businesslogic.controller.biometry.UserAuthenticationControllerImpl
import eu.europa.ec.businesslogic.controller.crypto.CryptoController
import eu.europa.ec.businesslogic.controller.crypto.CryptoControllerImpl
import eu.europa.ec.businesslogic.controller.crypto.KeystoreController
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
* Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
* except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the Licence for the specific language
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.businesslogic.model

import androidx.biometric.BiometricPrompt

data class BiometricCrypto(val cryptoObject: BiometricPrompt.CryptoObject?)
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package eu.europa.ec.commonfeature

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package eu.europa.ec.commonfeature.di

import eu.europa.ec.businesslogic.controller.authentication.UserAuthenticationController
import eu.europa.ec.businesslogic.controller.biometry.BiometricController
import eu.europa.ec.businesslogic.controller.biometry.UserAuthenticationController
import eu.europa.ec.businesslogic.controller.storage.PrefKeys
import eu.europa.ec.businesslogic.validator.FormValidator
import eu.europa.ec.commonfeature.interactor.BiometricInteractor
Expand Down
Loading

0 comments on commit 7894b3d

Please sign in to comment.