From 609397d95a73bdae55de41c47b19932e810d0320 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 1 May 2020 14:16:56 +0530 Subject: drastically reduce permissions for Web integration The old web integration with more permissions is still available on an opt-in basis. --- src/webex/messages.ts | 8 ++ src/webex/pageEntryPoint.ts | 4 + src/webex/pages/pay.tsx | 2 +- src/webex/pages/popup.tsx | 111 ++++++++++++++++++++++- src/webex/pages/welcome.tsx | 57 ++++++++++-- src/webex/pages/withdraw.tsx | 7 ++ src/webex/renderHtml.tsx | 4 +- src/webex/wxApi.ts | 15 ++++ src/webex/wxBackend.ts | 203 +++++++++++++++++++++++++++---------------- 9 files changed, 323 insertions(+), 88 deletions(-) (limited to 'src/webex') diff --git a/src/webex/messages.ts b/src/webex/messages.ts index 745e309c7..179eec88a 100644 --- a/src/webex/messages.ts +++ b/src/webex/messages.ts @@ -164,6 +164,14 @@ export interface MessageMap { request: {}; response: walletTypes.WalletDiagnostics; }; + "set-extended-permissions": { + request: { value: boolean }; + response: walletTypes.ExtendedPermissionsResponse; + }; + "get-extended-permissions": { + request: { }; + response: walletTypes.ExtendedPermissionsResponse; + }; } /** diff --git a/src/webex/pageEntryPoint.ts b/src/webex/pageEntryPoint.ts index dd9c13031..b9bdba06e 100644 --- a/src/webex/pageEntryPoint.ts +++ b/src/webex/pageEntryPoint.ts @@ -24,6 +24,7 @@ import ReactDOM from "react-dom"; import { createPopup } from "./pages/popup"; import { createWithdrawPage } from "./pages/withdraw"; import { createWelcomePage } from "./pages/welcome"; +import { createPayPage } from "./pages/pay"; function main(): void { try { @@ -43,6 +44,9 @@ function main(): void { case "welcome.html": mainElement = createWelcomePage(); break; + case "pay.html": + mainElement = createPayPage(); + break; default: throw Error(`page '${page}' not implemented`); } diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx index 61f287708..a69b6b766 100644 --- a/src/webex/pages/pay.tsx +++ b/src/webex/pages/pay.tsx @@ -178,7 +178,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element { ); } -export function makePayPage(): JSX.Element { +export function createPayPage(): JSX.Element { const url = new URL(document.location.href); const talerPayUri = url.searchParams.get("talerPayUri"); if (!talerPayUri) { diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx index 0fd2477f6..f6d95e2f9 100644 --- a/src/webex/pages/popup.tsx +++ b/src/webex/pages/popup.tsx @@ -34,11 +34,12 @@ import { WalletBalance, WalletBalanceEntry } from "../../types/walletTypes"; import { abbrev, renderAmount, PageLink } from "../renderHtml"; import * as wxApi from "../wxApi"; -import React, { Fragment } from "react"; +import React, { Fragment, useState, useEffect } from "react"; import { HistoryEvent } from "../../types/history"; import moment from "moment"; import { Timestamp } from "../../util/time"; +import { classifyTalerUri, TalerUriType } from "../../util/taleruri"; // FIXME: move to newer react functions /* eslint-disable react/no-deprecated */ @@ -761,7 +762,113 @@ function openTab(page: string) { }; } +function makeExtensionUrlWithParams( + url: string, + params?: { [name: string]: string | undefined }, +): string { + const innerUrl = new URL(chrome.extension.getURL("/" + url)); + if (params) { + for (const key in params) { + const p = params[key]; + if (p) { + innerUrl.searchParams.set(key, p); + } + } + } + return innerUrl.href; +} + +function actionForTalerUri(talerUri: string): string | undefined { + const uriType = classifyTalerUri(talerUri); + switch (uriType) { + case TalerUriType.TalerWithdraw: + return makeExtensionUrlWithParams("withdraw.html", { + talerWithdrawUri: talerUri, + }); + case TalerUriType.TalerPay: + return makeExtensionUrlWithParams("pay.html", { + talerPayUri: talerUri, + }); + case TalerUriType.TalerTip: + return makeExtensionUrlWithParams("tip.html", { + talerTipUri: talerUri, + }); + case TalerUriType.TalerRefund: + return makeExtensionUrlWithParams("refund.html", { + talerRefundUri: talerUri, + }); + case TalerUriType.TalerNotifyReserve: + // FIXME: implement + break; + default: + console.warn( + "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.", + ); + break; + } + return undefined; +} + +async function findTalerUriInActiveTab(): Promise { + return new Promise((resolve, reject) => { + chrome.tabs.executeScript( + { + code: ` + (() => { + let x = document.querySelector("a[href^='taler://'"); + return x ? x.href.toString() : null; + })(); + `, + allFrames: false, + }, + (result) => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError); + resolve(undefined); + return; + } + console.log("got result", result); + resolve(result[0]); + }, + ); + }); +} + function WalletPopup(): JSX.Element { + const [talerActionUrl, setTalerActionUrl] = useState( + undefined, + ); + const [dismissed, setDismissed] = useState(false); + useEffect(() => { + async function check(): Promise { + const talerUri = await findTalerUriInActiveTab(); + if (talerUri) { + const actionUrl = actionForTalerUri(talerUri); + setTalerActionUrl(actionUrl); + } + } + check(); + }); + if (talerActionUrl && !dismissed) { + return ( +
+

Taler Action

+

This page has a Taler action.

+

+ +

+

+ +

+
+ ); + } return (
@@ -777,6 +884,6 @@ function WalletPopup(): JSX.Element { } export function createPopup(): JSX.Element { - chrome.runtime.connect({ name: "popup" }); + //chrome.runtime.connect({ name: "popup" }); return ; } diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx index eecbe2be5..5092d2dd8 100644 --- a/src/webex/pages/welcome.tsx +++ b/src/webex/pages/welcome.tsx @@ -24,8 +24,9 @@ import React, { useState, useEffect } from "react"; import { getDiagnostics } from "../wxApi"; import { PageLink } from "../renderHtml"; import { WalletDiagnostics } from "../../types/walletTypes"; +import * as wxApi from "../wxApi"; -function Diagnostics(): JSX.Element { +function Diagnostics(): JSX.Element | null { const [timedOut, setTimedOut] = useState(false); const [diagnostics, setDiagnostics] = useState( undefined, @@ -55,7 +56,7 @@ function Diagnostics(): JSX.Element { if (diagnostics) { if (diagnostics.errors.length === 0) { - return

Running diagnostics ... everything looks fine.

; + return null; } else { return (
{ + const res = await wxApi.setExtendedPermissions(newVal); + setExtendedPermissions(res.newValue); + } + useEffect(() => { + async function getExtendedPermValue(): Promise { + const res = await wxApi.getExtendedPermissions() + setExtendedPermissions(res.newValue); + } + getExtendedPermValue(); + }); return ( <>

Thank you for installing the wallet.

-

First Steps

-

- Check out demo.taler.net for a - demo. -

-

Troubleshooting

+

Permissions

+
+ handleExtendedPerm(x.target.checked)} + type="checkbox" + id="checkbox-perm" + style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} + /> + + + (Enabling this option below will make using the wallet faster, but + requires more permissions from your browser.) + +
+

Next Steps

+ + Try the demo » + + + Learn how to top up your wallet balance » + ); } diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx index efd0adc86..1647a7065 100644 --- a/src/webex/pages/withdraw.tsx +++ b/src/webex/pages/withdraw.tsx @@ -160,11 +160,18 @@ function NewExchangeSelection(props: { return (
+

Digital Cash Withdrawal

You are about to withdraw{" "} {renderAmount(details.bankWithdrawDetails.amount)} from your bank account into your wallet. + { selectedExchange ? +

+ The exchange {selectedExchange} will be used as the Taler payment service provider. +

: null + } +