taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit 3c7308373cd4042bb9694233081971d4990b8d24
parent 3fa0612fae6cbd92701d04cd84ac60fccbac016e
Author: Iván Ávalos <avalos@disroot.org>
Date:   Thu,  6 Jun 2024 11:59:16 -0600

[wallet] Show warning when scanning unrelated QR from send/receive screens

bug 0008902

Diffstat:
Mwallet/src/main/java/net/taler/wallet/MainActivity.kt | 26+++++++++++++++++++++++++-
Mwallet/src/main/java/net/taler/wallet/MainViewModel.kt | 38+++++++++++++++++++++++++++++++++++++-
Mwallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt | 2+-
Mwallet/src/main/java/net/taler/wallet/SendFundsFragment.kt | 2+-
Mwallet/src/main/res/values/strings.xml | 6++++++
5 files changed, 70 insertions(+), 4 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt @@ -41,6 +41,7 @@ import androidx.navigation.ui.setupWithNavController import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener import com.google.zxing.client.android.Intents.Scan.MIXED_SCAN import com.google.zxing.client.android.Intents.Scan.SCAN_TYPE @@ -67,7 +68,11 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, private val barcodeLauncher = registerForActivityResult(ScanContract()) { result -> if (result == null || result.contents == null) return@registerForActivityResult - handleTalerUri(result.contents, "QR code") + if (model.checkScanQrContext(result.contents)) { + handleTalerUri(result.contents, "QR code") + } else { + confirmTalerUri(result.contents, "QR code") + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -175,6 +180,25 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, return super.onOptionsItemSelected(item) } + private fun confirmTalerUri(uri: String, from: String) { + MaterialAlertDialogBuilder(this).apply { + setTitle(R.string.qr_scan_context_title) + setMessage(when (model.getScanQrContext()) { + ScanQrContext.Send -> R.string.qr_scan_context_send_message + ScanQrContext.Receive -> R.string.qr_scan_context_receive_message + else -> error("invalid value") + }) + + setNegativeButton(R.string.ok) { _, _ -> + handleTalerUri(uri, from) + } + + setNeutralButton(R.string.cancel) { dialog, _ -> + dialog.dismiss() + } + }.show() + } + private fun handleTalerUri(uri: String, from: String) { val args = bundleOf("uri" to uri, "from" to from) nav.navigate(R.id.action_global_handle_uri, args) diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt @@ -17,6 +17,7 @@ package net.taler.wallet import android.app.Application +import android.net.Uri import android.util.Log import androidx.annotation.UiThread import androidx.lifecycle.AndroidViewModel @@ -68,6 +69,19 @@ private val observabilityNotifications = listOf( "request-observability-event", ) +private val sendUriActions = listOf( + "pay", + "tip", + "pay-pull", + "pay-template", +) + +private val receiveUriActions = listOf( + "withdraw", + "refund", + "pay-push", +) + class MainViewModel( app: Application, ) : AndroidViewModel(app), VersionReceiver, NotificationReceiver { @@ -116,6 +130,9 @@ class MainViewModel( private val mScanCodeEvent = MutableLiveData<Event<Boolean>>() val scanCodeEvent: LiveData<Event<Boolean>> = mScanCodeEvent + @set:Synchronized + private var scanQrContext = ScanQrContext.Unknown + override fun onVersionReceived(versionInfo: WalletCoreVersion) { walletVersion = versionInfo.implementationSemver walletVersionHash = versionInfo.implementationGitHash @@ -209,10 +226,23 @@ class MainViewModel( } @UiThread - fun scanCode() { + fun scanCode(context: ScanQrContext = ScanQrContext.Unknown) { + scanQrContext = context mScanCodeEvent.value = true.toEvent() } + fun getScanQrContext() = scanQrContext + + fun checkScanQrContext(uri: String): Boolean { + val parsed = Uri.parse(uri) + val action = parsed.host + return when (scanQrContext) { + ScanQrContext.Send -> action in sendUriActions + ScanQrContext.Receive -> action in receiveUriActions + else -> true + } + } + fun setDevMode(enabled: Boolean, onError: (error: TalerErrorInfo) -> Unit) { mDevMode.postValue(enabled) viewModelScope.launch { @@ -262,6 +292,12 @@ class MainViewModel( } +enum class ScanQrContext { + Send, + Receive, + Unknown, +} + sealed class AmountResult { class Success(val amount: Amount) : AmountResult() object InsufficientBalance : AmountResult() diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt @@ -125,7 +125,7 @@ class ReceiveFundsFragment : Fragment() { } private fun onScanQr() { - model.scanCode() + model.scanCode(ScanQrContext.Receive) } } diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt @@ -103,7 +103,7 @@ class SendFundsFragment : Fragment() { } private fun onScanQr() { - model.scanCode() + model.scanCode(ScanQrContext.Send) } } diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml @@ -72,6 +72,12 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="share_payment">Share payment link</string> <string name="uri_invalid">Not a valid Taler URI</string> + <!-- QR code handling --> + + <string name="qr_scan_context_title">Possibly unintended action</string> + <string name="qr_scan_context_receive_message">Seems like you were intending to receive money, but the QR code that you scanned corresponds to a different action. Do you wish to continue?</string> + <string name="qr_scan_context_send_message">Seems like you were intending to send money, but the QR code that you scanned corresponds to a different action. Do you wish to continue?</string> + <!-- Errors --> <string name="error_unsupported_uri">Error: This Taler URI is not supported.</string>