make permissions work for firefox

This commit is contained in:
Florian Dold 2020-06-03 16:21:09 +05:30
parent a3354306c6
commit e15e56c65a
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 124 additions and 34 deletions

View File

@ -33,3 +33,53 @@ export function isFirefox(): boolean {
export function isNode(): boolean { export function isNode(): boolean {
return typeof process !== "undefined" && process.release.name === "node"; return typeof process !== "undefined" && process.release.name === "node";
} }
/**
* Compatibility API that works on multiple browsers.
*/
export interface CrossBrowserPermissionsApi {
contains(
permissions: chrome.permissions.Permissions,
callback: (result: boolean) => void,
): void;
addPermissionsListener(
callback: (permissions: chrome.permissions.Permissions) => void,
): void;
request(
permissions: chrome.permissions.Permissions,
callback?: (granted: boolean) => void,
): void;
remove(
permissions: chrome.permissions.Permissions,
callback?: (removed: boolean) => void,
): void;
}
export function getPermissionsApi(): CrossBrowserPermissionsApi {
const myBrowser = (globalThis as any).browser;
if (
typeof myBrowser === "object" &&
typeof myBrowser.permissions === "object"
) {
return {
addPermissionsListener: () => {
// Not supported yet.
},
contains: myBrowser.permissions.contains,
request: myBrowser.permissions.request,
remove: myBrowser.permissions.remove,
};
} else {
return {
addPermissionsListener: chrome.permissions.onAdded.addListener.bind(
chrome.permissions.onAdded,
),
contains: chrome.permissions.contains,
request: chrome.permissions.request,
remove: chrome.permissions.remove,
};
}
}

View File

@ -25,6 +25,8 @@ import { getDiagnostics } from "../wxApi";
import { PageLink } from "../renderHtml"; import { PageLink } from "../renderHtml";
import { WalletDiagnostics } from "../../types/walletTypes"; import { WalletDiagnostics } from "../../types/walletTypes";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
import { getPermissionsApi } from "../compat";
import { extendedPermissions } from "../permissions";
function Diagnostics(): JSX.Element | null { function Diagnostics(): JSX.Element | null {
const [timedOut, setTimedOut] = useState(false); const [timedOut, setTimedOut] = useState(false);
@ -97,22 +99,51 @@ function Diagnostics(): JSX.Element | null {
} }
export function PermissionsCheckbox(): JSX.Element { export function PermissionsCheckbox(): JSX.Element {
const [extendedPermissions, setExtendedPermissions] = useState(false); const [extendedPermissionsEnabled, setExtendedPermissionsEnabled] = useState(
async function handleExtendedPerm(newVal: boolean): Promise<void> { false,
const res = await wxApi.setExtendedPermissions(newVal); );
setExtendedPermissions(res.newValue); async function handleExtendedPerm(requestedVal: boolean): Promise<void> {
let nextVal: boolean | undefined;
if (requestedVal) {
const granted = await new Promise<boolean>((resolve, reject) => {
// We set permissions here, since apparently FF wants this to be done
// as the result of an input event ...
getPermissionsApi().request(
extendedPermissions,
(granted: boolean) => {
if (chrome.runtime.lastError) {
console.error("error requesting permissions");
console.error(chrome.runtime.lastError);
reject(chrome.runtime.lastError);
return;
}
console.log("permissions granted:", granted);
resolve(granted);
},
);
});
const res = await wxApi.setExtendedPermissions(granted);
console.log(res);
nextVal = res.newValue;
} else {
const res = await wxApi.setExtendedPermissions(false);
console.log(res);
nextVal = res.newValue;
}
console.log("new permissions applied:", nextVal);
setExtendedPermissionsEnabled(nextVal ?? false);
} }
useEffect(() => { useEffect(() => {
async function getExtendedPermValue(): Promise<void> { async function getExtendedPermValue(): Promise<void> {
const res = await wxApi.getExtendedPermissions(); const res = await wxApi.getExtendedPermissions();
setExtendedPermissions(res.newValue); setExtendedPermissionsEnabled(res.newValue);
} }
getExtendedPermValue(); getExtendedPermValue();
}); });
return ( return (
<div> <div>
<input <input
checked={extendedPermissions} checked={extendedPermissionsEnabled}
onChange={(x) => handleExtendedPerm(x.target.checked)} onChange={(x) => handleExtendedPerm(x.target.checked)}
type="checkbox" type="checkbox"
id="checkbox-perm" id="checkbox-perm"

20
src/webex/permissions.ts Normal file
View File

@ -0,0 +1,20 @@
/*
This file is part of GNU Taler
(C) 2020 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
export const extendedPermissions = {
permissions: ["webRequest", "webRequestBlocking"],
origins: ["http://*/*", "https://*/*"],
};

View File

@ -40,11 +40,12 @@ import { BrowserHttpLib } from "../util/http";
import { OpenedPromise, openPromise } from "../util/promiseUtils"; import { OpenedPromise, openPromise } from "../util/promiseUtils";
import { classifyTalerUri, TalerUriType } from "../util/taleruri"; import { classifyTalerUri, TalerUriType } from "../util/taleruri";
import { Wallet } from "../wallet"; import { Wallet } from "../wallet";
import { isFirefox } from "./compat"; import { isFirefox, getPermissionsApi } from "./compat";
import { MessageType } from "./messages"; import { MessageType } from "./messages";
import * as wxApi from "./wxApi"; import * as wxApi from "./wxApi";
import MessageSender = chrome.runtime.MessageSender; import MessageSender = chrome.runtime.MessageSender;
import { Database } from "../util/query"; import { Database } from "../util/query";
import { extendedPermissions } from "./permissions";
const NeedsWallet = Symbol("NeedsWallet"); const NeedsWallet = Symbol("NeedsWallet");
@ -63,11 +64,6 @@ let outdatedDbVersion: number | undefined;
const walletInit: OpenedPromise<void> = openPromise<void>(); const walletInit: OpenedPromise<void> = openPromise<void>();
const extendedPermissions = {
permissions: ["webRequest", "webRequestBlocking"],
origins: ["http://*/*", "https://*/*"],
};
const notificationPorts: chrome.runtime.Port[] = []; const notificationPorts: chrome.runtime.Port[] = [];
async function handleMessage( async function handleMessage(
@ -216,7 +212,7 @@ async function handleMessage(
if (!proposalId) { if (!proposalId) {
throw Error("proposalId missing"); throw Error("proposalId missing");
} }
if (typeof proposalId !== "string") { if (typeof proposalId !== "string") {
throw Error("proposalId must be a string"); throw Error("proposalId must be a string");
} }
return needsWallet().getPurchaseDetails(proposalId); return needsWallet().getPurchaseDetails(proposalId);
@ -294,26 +290,13 @@ async function handleMessage(
return needsWallet().preparePayForUri(detail.talerPayUri); return needsWallet().preparePayForUri(detail.talerPayUri);
case "set-extended-permissions": { case "set-extended-permissions": {
const newVal = detail.value; const newVal = detail.value;
console.log("new extended permissions value", newVal);
if (newVal) { if (newVal) {
const res = await new Promise((resolve, reject) => { setupHeaderListener();
chrome.permissions.request( return { newValue: true };
extendedPermissions,
(granted: boolean) => {
console.log("permissions granted:", granted);
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
}
resolve(granted);
},
);
});
if (res) {
setupHeaderListener();
}
return { newValue: res };
} else { } else {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
chrome.permissions.remove(extendedPermissions, (rem) => { getPermissionsApi().remove(extendedPermissions, (rem) => {
console.log("permissions removed:", rem); console.log("permissions removed:", rem);
resolve(); resolve();
}); });
@ -323,7 +306,7 @@ async function handleMessage(
} }
case "get-extended-permissions": { case "get-extended-permissions": {
const res = await new Promise((resolve, reject) => { const res = await new Promise((resolve, reject) => {
chrome.permissions.contains(extendedPermissions, (result: boolean) => { getPermissionsApi().contains(extendedPermissions, (result: boolean) => {
resolve(result); resolve(result);
}); });
}); });
@ -590,7 +573,7 @@ function headerListener(
function setupHeaderListener(): void { function setupHeaderListener(): void {
console.log("setting up header listener"); console.log("setting up header listener");
// Handlers for catching HTTP requests // Handlers for catching HTTP requests
chrome.permissions.contains(extendedPermissions, (result: boolean) => { getPermissionsApi().contains(extendedPermissions, (result: boolean) => {
if ( if (
chrome.webRequest.onHeadersReceived && chrome.webRequest.onHeadersReceived &&
chrome.webRequest.onHeadersReceived.hasListener(headerListener) chrome.webRequest.onHeadersReceived.hasListener(headerListener)
@ -644,9 +627,15 @@ export async function wxMain(): Promise<void> {
}); });
}); });
setupHeaderListener(); try {
setupHeaderListener();
} catch (e) {
console.log(e);
}
chrome.permissions.onAdded.addListener((perm) => { // On platforms that support it, also listen to external
// modification of permissions.
getPermissionsApi().addPermissionsListener((perm) => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError); console.error(chrome.runtime.lastError);
return; return;