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

ABW-2488 - Refactor around dApp Login Auth and UnAuth #747

Closed
wants to merge 1 commit into from
Closed
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
@@ -0,0 +1,195 @@
package com.babylon.wallet.android.presentation.dapp

import com.babylon.wallet.android.data.dapp.DappMessenger
import com.babylon.wallet.android.data.dapp.IncomingRequestRepository
import com.babylon.wallet.android.data.repository.state.StateRepository
import com.babylon.wallet.android.domain.RadixWalletException
import com.babylon.wallet.android.domain.getDappMessage
import com.babylon.wallet.android.domain.usecases.BuildAuthorizedDappResponseUseCase
import com.babylon.wallet.android.domain.usecases.BuildUnauthorizedDappResponseUseCase
import com.babylon.wallet.android.presentation.common.UiMessage
import com.babylon.wallet.android.presentation.common.ViewModelDelegate
import com.babylon.wallet.android.presentation.dapp.authorized.login.DAppLoginUiState
import com.babylon.wallet.android.presentation.dapp.authorized.login.Event
import com.babylon.wallet.android.presentation.dapp.authorized.selectpersona.toUiModel
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import rdx.works.profile.data.model.pernetwork.Network
import rdx.works.profile.data.repository.DAppConnectionRepository
import rdx.works.profile.domain.ProfileException
import rdx.works.profile.domain.gateway.GetCurrentGatewayUseCase
import javax.inject.Inject

class DAppLoginDelegate @Inject constructor(
private val dAppMessenger: DappMessenger,
private val dAppConnectionRepository: DAppConnectionRepository,
private val getCurrentGatewayUseCase: GetCurrentGatewayUseCase,
private val stateRepository: StateRepository,
private val incomingRequestRepository: IncomingRequestRepository,
private val buildAuthorizedDappResponseUseCase: BuildAuthorizedDappResponseUseCase,
private val buildUnauthorizedDappResponseUseCase: BuildUnauthorizedDappResponseUseCase
) : ViewModelDelegate<DAppLoginUiState>() {

suspend fun processLoginRequest(
requestId: String,
sendEvent: (Event) -> Unit,
isAuthorizedRequest: Boolean
) {
_state.update { it.copy(requestId = requestId) }

if (isAuthorizedRequest) {
val requestToHandle = incomingRequestRepository.getAuthorizedRequest(requestId)
if (requestToHandle == null) {
sendEvent(Event.CloseLoginFlow)
return
} else {
_state.update { it.copy(authorizeRequest = requestToHandle) }
}

val currentNetworkId = getCurrentGatewayUseCase().network.networkId().value
if (currentNetworkId != requestToHandle.requestMetadata.networkId) {
handleRequestError(
exception = RadixWalletException.DappRequestException.WrongNetwork(
currentNetworkId,
requestToHandle.requestMetadata.networkId
),
isAuthorizedRequest = true
)
return
}
if (!requestToHandle.isValidRequest()) {
handleRequestError(
exception = RadixWalletException.DappRequestException.InvalidRequest,
isAuthorizedRequest = true
)
return
}

val authorizedDApp = dAppConnectionRepository.getAuthorizedDapp(
requestToHandle.requestMetadata.dAppDefinitionAddress
)

_state.update { it.copy(authorizedDApp = authorizedDApp) }
_state.update { it.copy(editedDApp = authorizedDApp) }

stateRepository.getDAppsDetails(
definitionAddresses = listOf(requestToHandle.metadata.dAppDefinitionAddress),
skipCache = false
).onSuccess { dApps ->
dApps.firstOrNull()?.let { dApp ->
_state.update { it.copy(dapp = dApp) }
}
}.onFailure { error ->
_state.update { it.copy(uiMessage = UiMessage.ErrorMessage(error)) }
}
} else {
val requestToHandle = incomingRequestRepository.getUnauthorizedRequest(requestId)
if (requestToHandle == null) {
sendEvent(Event.CloseLoginFlow)
return
} else {
_state.update { it.copy(unAuthorizeRequest = requestToHandle) }
}

val currentNetworkId = getCurrentGatewayUseCase().network.networkId().value
if (currentNetworkId != requestToHandle.requestMetadata.networkId) {
handleRequestError(
exception = RadixWalletException.DappRequestException.WrongNetwork(
currentNetworkId,
requestToHandle.requestMetadata.networkId
),
isAuthorizedRequest = false
)
return
}
if (!requestToHandle.isValidRequest()) {
handleRequestError(
exception = RadixWalletException.DappRequestException.InvalidRequest,
isAuthorizedRequest = false
)
return
}

stateRepository.getDAppsDetails(
definitionAddresses = listOf(requestToHandle.metadata.dAppDefinitionAddress),
skipCache = false
).onSuccess { dApps ->
dApps.firstOrNull()?.let { dApp ->
_state.update { it.copy(dapp = dApp) }
}
}.onFailure { error ->
_state.update { it.copy(uiMessage = UiMessage.ErrorMessage(error)) }
}
}
}

suspend fun handleRequestError(exception: Throwable, isAuthorizedRequest: Boolean) {
if (exception is RadixWalletException.DappRequestException) {
if (isAuthorizedRequest) {
if (exception.cause is RadixWalletException.LedgerCommunicationException) {
return
}
} else {
if (exception is RadixWalletException.LedgerCommunicationException.FailedToSignAuthChallenge) {
return
}
}
if (exception.cause is RadixWalletException.SignatureCancelled) {
return
}
if (exception.cause is ProfileException.NoMnemonic) {
_state.update { it.copy(isNoMnemonicErrorVisible = true) }
return
}
dAppMessenger.sendWalletInteractionResponseFailure(
remoteConnectorId = _state.value.remoteConnectionId,
requestId = _state.value.requestId.orEmpty(),
error = exception.ceError,
message = exception.getDappMessage()
)
_state.update { it.copy(failureDialog = FailureDialogState.Open(exception)) }
} else {
if (isAuthorizedRequest.not() && exception is ProfileException.NoMnemonic) {
_state.update { it.copy(isNoMnemonicErrorVisible = true) }
}
}
}

fun observeSigningState(isAuthorizedRequest: Boolean) {
viewModelScope.launch {
if (isAuthorizedRequest) {
buildAuthorizedDappResponseUseCase.signingState
} else {
buildUnauthorizedDappResponseUseCase.signingState
}.collect { signingState ->
_state.update { state ->
state.copy(interactionState = signingState)
}
}
}
}

fun onAcknowledgeFailureDialog(
sendEvent: (Event) -> Unit
) = viewModelScope.launch {
_state.update { it.copy(failureDialog = FailureDialogState.Closed) }
sendEvent(Event.CloseLoginFlow)
incomingRequestRepository.requestHandled(requestId = _state.value.requestId.orEmpty())
}

fun onDismissSigningStatusDialog() {
_state.update { it.copy(interactionState = null) }
}

fun onSelectPersona(persona: Network.Persona) {
_state.update { it.copy(selectedPersona = persona.toUiModel()) }
}

fun onMessageShown() {
_state.update { it.copy(uiMessage = null) }
}

fun dismissNoMnemonicError() {
_state.update { it.copy(isNoMnemonicErrorVisible = false) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.babylon.wallet.android.presentation.dapp

import com.babylon.wallet.android.domain.model.RequiredPersonaFields
import com.babylon.wallet.android.presentation.common.OneOffEvent

sealed interface DAppLoginEvent : OneOffEvent {

data object CloseLoginFlow : DAppLoginEvent
data class RequestCompletionBiometricPrompt(val isSignatureRequired: Boolean) : DAppLoginEvent

data object LoginFlowCompleted : DAppLoginEvent

data class DisplayPermission(
val numberOfAccounts: Int,
val isExactAccountsCount: Boolean,
val oneTime: Boolean = false
) : DAppLoginEvent

data class PersonaDataOngoing(
val personaAddress: String,
val requiredPersonaFields: RequiredPersonaFields
) : DAppLoginEvent

data class PersonaDataOnetime(val requiredPersonaFields: RequiredPersonaFields) : DAppLoginEvent

data class ChooseAccounts(
val numberOfAccounts: Int,
val isExactAccountsCount: Boolean,
val oneTime: Boolean = false,
val showBack: Boolean = true
) : DAppLoginEvent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.babylon.wallet.android.presentation.dapp

import com.babylon.wallet.android.data.transaction.InteractionState
import com.babylon.wallet.android.domain.model.DApp
import com.babylon.wallet.android.domain.model.MessageFromDataChannel
import com.babylon.wallet.android.presentation.common.UiMessage
import com.babylon.wallet.android.presentation.common.UiState
import com.babylon.wallet.android.presentation.dapp.authorized.account.AccountItemUiModel
import com.babylon.wallet.android.presentation.dapp.authorized.selectpersona.PersonaUiModel
import rdx.works.profile.data.model.pernetwork.Network
import rdx.works.profile.data.model.pernetwork.PersonaData

data class DAppLoginUiState(
val dapp: DApp? = null,
val uiMessage: UiMessage? = null,
val failureDialog: FailureDialogState = FailureDialogState.Closed,
val initialAuthorizedLoginRoute: InitialAuthorizedLoginRoute? = null,
val initialUnauthorizedLoginRoute: InitialUnauthorizedLoginRoute? = null,
val selectedAccountsOngoing: List<AccountItemUiModel> = emptyList(),
val selectedOngoingPersonaData: PersonaData? = null,
val selectedOneTimePersonaData: PersonaData? = null,
val selectedAccountsOneTime: List<AccountItemUiModel> = emptyList(),
val selectedPersona: PersonaUiModel? = null,
val interactionState: InteractionState? = null,
val isNoMnemonicErrorVisible: Boolean = false,
val isAuthorizeRequest: Boolean = false,
var authorizeRequest: MessageFromDataChannel.IncomingRequest.AuthorizedRequest? = null,
var unAuthorizeRequest: MessageFromDataChannel.IncomingRequest.UnauthorizedRequest? = null,
var requestId: String? = null,
var authorizedDApp: Network.AuthorizedDapp? = null,
var editedDApp: Network.AuthorizedDapp? = null
) : UiState {

val remoteConnectionId: String
get() = if (isAuthorizeRequest) {
authorizeRequest?.remoteConnectorId.orEmpty()
} else {
unAuthorizeRequest?.remoteConnectorId.orEmpty()
}
}
Loading
Loading