Skip to content

Commit

Permalink
GetPasswordCredentialRequest handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Nailik committed Oct 20, 2024
1 parent c74bc86 commit 4aa9f5a
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import androidx.annotation.RequiresApi
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.autofill.password.processor.PasswordProviderProcessor
import com.x8bit.bitwarden.data.autofill.password.processor.PasswordProviderProcessorImpl
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import dagger.Module
import dagger.Provides
Expand All @@ -30,15 +30,15 @@ object PasswordProviderModule {
fun providePasswordCredentialProviderProcessor(
@ApplicationContext context: Context,
authRepository: AuthRepository,
vaultRepository: VaultRepository,
dispatcherManager: DispatcherManager,
autofillCipherProvider: AutofillCipherProvider,
intentManager: IntentManager,
clock: Clock,
): PasswordProviderProcessor =
PasswordProviderProcessorImpl(
context,
authRepository,
vaultRepository,
autofillCipherProvider,
intentManager,
clock,
dispatcherManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class PasswordGetCredentialsRequest(
val candidateQueryData: Bundle,
val id: String,
val userId: String,
val cipherId: String,
val allowedUserIds: Set<String>,
val packageName: String,
val signingInfo: SigningInfo,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.x8bit.bitwarden.data.autofill.password.model

import androidx.credentials.provider.BeginGetPasswordOption
import com.bitwarden.vault.LoginView

/**
* Represents the result of a FIDO 2 Get Credentials request.
* Represents the result of a Password Get Credentials request.
*/
sealed class PasswordGetCredentialsResult {
/**
* Indicates credentials were successfully queried.
*/
data class Success(
val data: String
val userId: String,
val option: BeginGetPasswordOption,
val credential: LoginView,
) : PasswordGetCredentialsResult()

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import androidx.credentials.provider.CreateEntry
import androidx.credentials.provider.CredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.ProviderClearCredentialStateRequest
import com.bitwarden.core.Uuid
import com.bitwarden.fido.Fido2CredentialAutofillView
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.base.util.toAndroidAppUriString
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicInteger

private const val CREATE_PASSWORD_INTENT = "com.x8bit.bitwarden.data.autofill.password.ACTION_CREATE_PASSWORD"
const val GET_PASSWORD_INTENT = "com.x8bit.bitwarden.data.autofill.password.ACTION_GET_PASSWORD"
const val UNLOCK_ACCOUNT_INTENT= "com.x8bit.bitwarden.data.autofill.password.ACTION_UNLOCK_ACCOUNT"
const val UNLOCK_ACCOUNT_INTENT = "com.x8bit.bitwarden.data.autofill.password.ACTION_UNLOCK_ACCOUNT"

/**
* The default implementation of [PasswordProviderProcessor]. Its purpose is to handle Password related
Expand All @@ -49,7 +49,7 @@ const val UNLOCK_ACCOUNT_INTENT= "com.x8bit.bitwarden.data.autofill.password.ACT
class PasswordProviderProcessorImpl(
private val context: Context,
private val authRepository: AuthRepository,
private val vaultRepository: VaultRepository,
private val autofillCipherProvider: AutofillCipherProvider,
private val intentManager: IntentManager,
private val clock: Clock,
dispatcherManager: DispatcherManager,
Expand Down Expand Up @@ -187,7 +187,7 @@ class PasswordProviderProcessorImpl(
}

@Throws(GetCredentialUnsupportedException::class)
private fun getMatchingPasswordCredentialEntries(
private suspend fun getMatchingPasswordCredentialEntries(
userId: String,
request: BeginGetCredentialRequest,
): List<CredentialEntry> =
Expand All @@ -198,7 +198,9 @@ class PasswordProviderProcessorImpl(
if (option.allowedUserIds.isEmpty() || option.allowedUserIds.contains(userId)) {
buildCredentialEntries(
userId = userId,
callingPackage = request.callingAppInfo?.packageName,
matchUri = request.callingAppInfo?.origin
?: request.callingAppInfo?.packageName
?.toAndroidAppUriString(),
option = option,
)
} else {
Expand All @@ -210,43 +212,35 @@ class PasswordProviderProcessorImpl(
}
}

private fun buildCredentialEntries(
private suspend fun buildCredentialEntries(
userId: String,
callingPackage: String?,
matchUri: String?,
option: BeginGetPasswordOption,
): List<CredentialEntry> {
//TODO get data and map correctly
return listOf(
Fido2CredentialAutofillView(
credentialId = ByteArray(0),
cipherId = Uuid(),
rpId = "",
userNameForUi = "userNameForUi",
userHandle = ByteArray(0)
)
return autofillCipherProvider.getLoginAutofillCiphers(
uri = matchUri ?: return emptyList(),
).toCredentialEntries(
userId = userId,
option = option,
)
}


private fun List<Fido2CredentialAutofillView>.toCredentialEntries(
private fun List<AutofillCipher.Login>.toCredentialEntries(
userId: String,
option: BeginGetPasswordOption,
): List<CredentialEntry> =
this
.map {
.mapNotNull {
PasswordCredentialEntry
.Builder(
context = context,
username = it.userNameForUi ?: context.getString(R.string.no_username),
username = it.username,
pendingIntent = intentManager
.createPasswordGetCredentialPendingIntent(
action = GET_PASSWORD_INTENT,
id = option.id,
userId = userId,
credentialId = "",
cipherId = it.cipherId,
cipherId = it.cipherId ?: return@mapNotNull null,
requestCode = requestCode.getAndIncrement(),
),
beginGetPasswordOption = option,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package com.x8bit.bitwarden.data.autofill.password.util
import android.content.Intent
import android.os.Build
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.provider.BeginGetPasswordOption
import androidx.credentials.GetPasswordOption
import androidx.credentials.provider.PendingIntentHandler
import com.x8bit.bitwarden.data.autofill.password.model.PasswordCredentialRequest
import com.x8bit.bitwarden.data.autofill.password.model.PasswordGetCredentialsRequest
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import com.x8bit.bitwarden.ui.platform.manager.intent.EXTRA_KEY_CIPHER_ID
import com.x8bit.bitwarden.ui.platform.manager.intent.EXTRA_KEY_PASSWORD_CREDENTIAL_ID
import com.x8bit.bitwarden.ui.platform.manager.intent.EXTRA_KEY_USER_ID

/**
Expand Down Expand Up @@ -47,25 +49,31 @@ fun Intent.getPasswordGetCredentialsRequestOrNull(): PasswordGetCredentialsReque
if (isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) return null

val systemRequest = PendingIntentHandler
.retrieveBeginGetCredentialRequest(this)
.retrieveProviderGetCredentialRequest(this)
?: return null

val option: BeginGetPasswordOption = systemRequest
.beginGetCredentialOptions
.firstNotNullOfOrNull { it as? BeginGetPasswordOption }
val option: GetPasswordOption = systemRequest
.credentialOptions
.firstNotNullOfOrNull { it as? GetPasswordOption }
?: return null

val callingAppInfo = systemRequest
.callingAppInfo

val cipherId = getStringExtra(EXTRA_KEY_CIPHER_ID)
?: return null

val userId: String = getStringExtra(EXTRA_KEY_USER_ID)
?: return null

val id: String = getStringExtra(EXTRA_KEY_PASSWORD_CREDENTIAL_ID)
?: return null

return PasswordGetCredentialsRequest(
candidateQueryData = option.candidateQueryData,
id = option.id,
id = id,
userId = userId,
cipherId = cipherId,
allowedUserIds = option.allowedUserIds,
packageName = callingAppInfo.packageName,
signingInfo = callingAppInfo.signingInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.credentials.CreatePasswordResponse
import androidx.credentials.GetCredentialResponse
import androidx.credentials.PasswordCredential
import androidx.credentials.exceptions.CreateCredentialUnknownException
import androidx.credentials.exceptions.GetCredentialUnknownException
import androidx.credentials.provider.PendingIntentHandler
import com.x8bit.bitwarden.data.autofill.password.model.PasswordGetCredentialsResult
import com.x8bit.bitwarden.data.autofill.password.model.PasswordRegisterCredentialResult
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager

/**
* Primary implementation of [PasswordCompletionManager] when the build version is
Expand All @@ -18,7 +20,6 @@ import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
class PasswordCompletionManagerImpl(
private val activity: Activity,
private val intentManager: IntentManager,
) : PasswordCompletionManager {

override fun completePasswordRegistration(result: PasswordRegisterCredentialResult) {
Expand Down Expand Up @@ -47,6 +48,38 @@ class PasswordCompletionManagerImpl(
}

override fun completePasswordGetCredentialRequest(result: PasswordGetCredentialsResult) {
TODO()
val resultIntent = Intent()
when (result) {
is PasswordGetCredentialsResult.Success -> {
val userName = result.credential.username
val password = result.credential.password

if (userName != null && password != null) {
PendingIntentHandler
.setGetCredentialResponse(
resultIntent,
response = GetCredentialResponse(
credential = PasswordCredential(
id = userName,
password = password,
),
),
)
} else {
PendingIntentHandler.setGetCredentialException(
resultIntent,
GetCredentialUnknownException(),
)
}
}
PasswordGetCredentialsResult.Error -> {
PendingIntentHandler.setGetCredentialException(
resultIntent,
GetCredentialUnknownException(),
)
}
}
activity.setResult(Activity.RESULT_OK, resultIntent)
activity.finish()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ interface IntentManager {
*/
fun createPasswordGetCredentialPendingIntent(
action: String,
id: String,
userId: String,
credentialId: String,
cipherId: String,
requestCode: Int,
): PendingIntent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ const val EXTRA_KEY_CREDENTIAL_ID: String = "credential_id"
*/
const val EXTRA_KEY_CIPHER_ID: String = "cipher_id"

/**
* Key for the credential id included in Password provider "get entries".
*
* @see IntentManager.createPasswordGetCredentialPendingIntent
*/
const val EXTRA_KEY_PASSWORD_CREDENTIAL_ID: String = "password_credential_id"

/**
* The default implementation of the [IntentManager] for simplifying the handling of Android
* Intents within a given context.
Expand Down Expand Up @@ -313,15 +320,15 @@ class IntentManagerImpl(

override fun createPasswordGetCredentialPendingIntent(
action: String,
id: String,
userId: String,
credentialId: String,
cipherId: String,
requestCode: Int
): PendingIntent {
val intent = Intent(action)
.setPackage(context.packageName)
.putExtra(EXTRA_KEY_USER_ID, userId)
.putExtra(EXTRA_KEY_CREDENTIAL_ID, credentialId)
.putExtra(EXTRA_KEY_PASSWORD_CREDENTIAL_ID, id)
.putExtra(EXTRA_KEY_CIPHER_ID, cipherId)

return PendingIntent.getActivity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.autofill.fido2.manager.Fido2CompletionManager
import com.x8bit.bitwarden.ui.autofill.password.manager.PasswordCompletionManager
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenAccountActionItem
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenAccountSwitcher
Expand All @@ -49,6 +50,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager
import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager
import com.x8bit.bitwarden.ui.platform.composition.LocalFido2CompletionManager
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
import com.x8bit.bitwarden.ui.platform.composition.LocalPasswordCompletionManager
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.PinInputDialog
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
Expand Down Expand Up @@ -79,6 +81,7 @@ fun VaultItemListingScreen(
intentManager: IntentManager = LocalIntentManager.current,
exitManager: ExitManager = LocalExitManager.current,
fido2CompletionManager: Fido2CompletionManager = LocalFido2CompletionManager.current,
passwordCompletionManager: PasswordCompletionManager = LocalPasswordCompletionManager.current,
biometricsManager: BiometricsManager = LocalBiometricsManager.current,
viewModel: VaultItemListingViewModel = hiltViewModel(),
) {
Expand Down Expand Up @@ -173,6 +176,10 @@ fun VaultItemListingScreen(
fido2CompletionManager.completeFido2GetCredentialRequest(event.result)
}

is VaultItemListingEvent.CompletePasswordGetCredentialsRequest -> {
passwordCompletionManager.completePasswordGetCredentialRequest(event.result)
}

VaultItemListingEvent.ExitApp -> exitManager.exitApplication()
}
}
Expand Down
Loading

0 comments on commit 4aa9f5a

Please sign in to comment.