mirror of
https://github.com/bitwarden/android.git
synced 2025-12-11 04:39:19 -06:00
[PM-25662] Add CredentialExchangeCompletionManager (#5867)
This commit is contained in:
parent
1dc6ea2227
commit
481a8c8fbc
@ -0,0 +1,16 @@
|
||||
package com.bitwarden.cxf.manager
|
||||
|
||||
import com.bitwarden.cxf.manager.model.ExportCredentialsResult
|
||||
|
||||
/**
|
||||
* A manager for completing the Credential Exchange processes.
|
||||
*/
|
||||
interface CredentialExchangeCompletionManager {
|
||||
|
||||
/**
|
||||
* Complete the Credential Exchange export process with the provided [exportResult].
|
||||
*
|
||||
* @param exportResult The result of the export operation.
|
||||
*/
|
||||
fun completeCredentialExport(exportResult: ExportCredentialsResult)
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package com.bitwarden.cxf.manager
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.credentials.providerevents.playservices.IntentHandler
|
||||
import androidx.credentials.providerevents.transfer.ImportCredentialsResponse
|
||||
import com.bitwarden.cxf.manager.model.ExportCredentialsResult
|
||||
|
||||
/**
|
||||
* Primary implementation of [CredentialExchangeCompletionManager].
|
||||
*/
|
||||
internal class CredentialExchangeCompletionManagerImpl(
|
||||
private val activity: Activity,
|
||||
) : CredentialExchangeCompletionManager {
|
||||
|
||||
override fun completeCredentialExport(exportResult: ExportCredentialsResult) {
|
||||
val intent = Intent()
|
||||
when (exportResult) {
|
||||
is ExportCredentialsResult.Failure -> {
|
||||
IntentHandler.setImportCredentialsException(
|
||||
intent = intent,
|
||||
exception = exportResult.error,
|
||||
)
|
||||
}
|
||||
|
||||
is ExportCredentialsResult.Success -> {
|
||||
IntentHandler.setImportCredentialsResponse(
|
||||
context = activity,
|
||||
uri = exportResult.uri,
|
||||
response = ImportCredentialsResponse(
|
||||
responseJson = exportResult.payload,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
activity.apply {
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
@file:OmitFromCoverage
|
||||
|
||||
package com.bitwarden.cxf.manager.dsl
|
||||
|
||||
import android.app.Activity
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.cxf.manager.CredentialExchangeCompletionManager
|
||||
import com.bitwarden.cxf.manager.CredentialExchangeCompletionManagerImpl
|
||||
|
||||
/**
|
||||
* A DSL for building a [CredentialExchangeCompletionManager].
|
||||
*
|
||||
* This class provides a structured way to configure and create an instance of
|
||||
* [CredentialExchangeCompletionManager], which is used to finalize the credential
|
||||
* exchange process by returning a result to the calling application. It is primarily
|
||||
* used within the [credentialExchangeCompletionManager] builder function.
|
||||
*
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
class CredentialExchangeCompletionManagerBuilder internal constructor() {
|
||||
internal fun build(activity: Activity): CredentialExchangeCompletionManager =
|
||||
CredentialExchangeCompletionManagerImpl(activity = activity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of [CredentialExchangeCompletionManager] using a DSL-style builder.
|
||||
*
|
||||
* This function is the entry point for handling the completion of a credential exchange flow,
|
||||
* such as after a user has successfully created or selected a passkey.
|
||||
*
|
||||
* Example usage:
|
||||
* ```
|
||||
* val completionManager = credentialExchangeCompletionManager(activity) {
|
||||
* // Configuration options can be added here if the DSL is extended in the future.
|
||||
* }
|
||||
*
|
||||
* // Use the completionManager to finish the credential exchange.
|
||||
* completionManager.completeCredentialExport(...)
|
||||
* ```
|
||||
*
|
||||
* @param activity The [Activity] that initiated the credential exchange operation. This is
|
||||
* used to send back the result to the calling application (e.g., the browser).
|
||||
* @param config A lambda with [CredentialExchangeCompletionManagerBuilder] as its receiver,
|
||||
* allowing for declarative configuration of the manager.
|
||||
*
|
||||
* @return A configured [CredentialExchangeCompletionManager] instance.
|
||||
*/
|
||||
fun credentialExchangeCompletionManager(
|
||||
activity: Activity,
|
||||
config: CredentialExchangeCompletionManagerBuilder.() -> Unit = {},
|
||||
): CredentialExchangeCompletionManager =
|
||||
CredentialExchangeCompletionManagerBuilder()
|
||||
.apply(config)
|
||||
.build(activity = activity)
|
||||
@ -0,0 +1,20 @@
|
||||
package com.bitwarden.cxf.manager.model
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.credentials.providerevents.exception.ImportCredentialsException
|
||||
|
||||
/**
|
||||
* Represents the result of exporting credentials.
|
||||
*/
|
||||
sealed class ExportCredentialsResult {
|
||||
|
||||
/**
|
||||
* Represents a successful export.
|
||||
*/
|
||||
data class Success(val payload: String, val uri: Uri) : ExportCredentialsResult()
|
||||
|
||||
/**
|
||||
* Represents a failure to export.
|
||||
*/
|
||||
data class Failure(val error: ImportCredentialsException) : ExportCredentialsResult()
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
@file:OmitFromCoverage
|
||||
|
||||
package com.bitwarden.cxf.ui.composition
|
||||
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.cxf.manager.CredentialExchangeCompletionManager
|
||||
|
||||
/**
|
||||
* Provides access to the Credential Exchange completion manager throughout the app.
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
val LocalCredentialExchangeCompletionManager: ProvidableCompositionLocal<CredentialExchangeCompletionManager> =
|
||||
compositionLocalOf {
|
||||
error("CompositionLocal LocalPermissionsManager not present")
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.bitwarden.cxf.manager
|
||||
|
||||
import android.app.Activity
|
||||
import android.net.Uri
|
||||
import androidx.credentials.providerevents.exception.ImportCredentialsException
|
||||
import androidx.credentials.providerevents.playservices.IntentHandler
|
||||
import com.bitwarden.cxf.manager.model.ExportCredentialsResult
|
||||
import io.mockk.Ordering
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CredentialExchangeCompletionManagerTest {
|
||||
|
||||
private val mockActivity = mockk<Activity>()
|
||||
private val completionManager = CredentialExchangeCompletionManagerImpl(mockActivity)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkObject(IntentHandler)
|
||||
every {
|
||||
IntentHandler.setImportCredentialsResponse(
|
||||
context = any(),
|
||||
uri = any(),
|
||||
response = any(),
|
||||
)
|
||||
} just runs
|
||||
|
||||
every {
|
||||
IntentHandler.setImportCredentialsException(
|
||||
intent = any(),
|
||||
exception = any(),
|
||||
)
|
||||
} just runs
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `completeCredentialExport sets Success result and finishes the activity`() {
|
||||
val mockUri = mockk<Uri>()
|
||||
val exportResult = ExportCredentialsResult.Success("payload", mockUri)
|
||||
|
||||
every { mockActivity.setResult(Activity.RESULT_OK, any()) } just runs
|
||||
every { mockActivity.finish() } just runs
|
||||
|
||||
completionManager.completeCredentialExport(exportResult)
|
||||
|
||||
verify(ordering = Ordering.ORDERED) {
|
||||
IntentHandler.setImportCredentialsResponse(
|
||||
context = mockActivity,
|
||||
uri = mockUri,
|
||||
response = any(),
|
||||
)
|
||||
mockActivity.setResult(Activity.RESULT_OK, any())
|
||||
mockActivity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `completeCredentialExport sets Failure result and finishes the activity`() {
|
||||
val importException = mockk<ImportCredentialsException>()
|
||||
val exportResult = ExportCredentialsResult.Failure(error = importException)
|
||||
|
||||
every { mockActivity.setResult(Activity.RESULT_OK, any()) } just runs
|
||||
every { mockActivity.finish() } just runs
|
||||
|
||||
completionManager.completeCredentialExport(exportResult)
|
||||
|
||||
verify(ordering = Ordering.ORDERED) {
|
||||
IntentHandler.setImportCredentialsException(
|
||||
intent = any(),
|
||||
exception = importException,
|
||||
)
|
||||
mockActivity.setResult(Activity.RESULT_OK, any())
|
||||
mockActivity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user