[tbb-commits] [tor-browser/tor-browser-68.5.0esr-9.5-1] squash! Bug 30237: Add v3 onion services client authentication prompt
sysrqb at torproject.org
sysrqb at torproject.org
Fri Feb 21 21:46:34 UTC 2020
commit aed69dc95387429e18b18ad578fb78d4a83d91f2
Author: Kathy Brade <brade at pearlcrescent.com>
Date: Thu Feb 13 15:06:38 2020 -0500
squash! Bug 30237: Add v3 onion services client authentication prompt
Also fixes bug 19757:
Add a "Remember this key" checkbox to the client auth prompt.
Add an "Onion Services Authentication" section within the
about:preferences "Privacy & Security section" to allow
viewing and removal of v3 onion client auth keys that have
been stored on disk.
---
.../onionservices/content/authPopup.inc.xul | 2 +
.../onionservices/content/authPreferences.css | 20 ++
.../onionservices/content/authPreferences.inc.xul | 20 ++
.../onionservices/content/authPreferences.js | 63 +++++
.../components/onionservices/content/authPrompt.js | 40 ++--
.../components/onionservices/content/authUtil.jsm | 27 ++-
.../onionservices/content/savedKeysDialog.js | 259 +++++++++++++++++++++
.../onionservices/content/savedKeysDialog.xul | 42 ++++
browser/components/onionservices/jar.mn | 4 +
.../preferences/in-content/preferences.xul | 1 +
.../components/preferences/in-content/privacy.js | 7 +
.../components/preferences/in-content/privacy.xul | 2 +
browser/modules/TorStrings.jsm | 15 +-
13 files changed, 477 insertions(+), 25 deletions(-)
diff --git a/browser/components/onionservices/content/authPopup.inc.xul b/browser/components/onionservices/content/authPopup.inc.xul
index d327e4c6a88d..bd0ec3aa0b00 100644
--- a/browser/components/onionservices/content/authPopup.inc.xul
+++ b/browser/components/onionservices/content/authPopup.inc.xul
@@ -9,6 +9,8 @@
<html:div>
<html:input id="tor-clientauth-notification-key" type="password"/>
<html:div id="tor-clientauth-warning"/>
+ <checkbox id="tor-clientauth-persistkey-checkbox"
+ label="&torbutton.onionServices.authPrompt.persistCheckboxLabel;"/>
</html:div>
</popupnotificationcontent>
</popupnotification>
diff --git a/browser/components/onionservices/content/authPreferences.css b/browser/components/onionservices/content/authPreferences.css
new file mode 100644
index 000000000000..b3fb79b26ddc
--- /dev/null
+++ b/browser/components/onionservices/content/authPreferences.css
@@ -0,0 +1,20 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+#torOnionServiceKeys-overview-container {
+ margin-right: 30px;
+}
+
+#onionservices-savedkeys-tree treechildren::-moz-tree-cell-text {
+ font-size: 80%;
+}
+
+#onionservices-savedkeys-errorContainer {
+ margin-top: 4px;
+ min-height: 3em;
+}
+
+#onionservices-savedkeys-errorIcon {
+ margin-right: 4px;
+ list-style-image: url("chrome://browser/skin/warning.svg");
+ visibility: hidden;
+}
diff --git a/browser/components/onionservices/content/authPreferences.inc.xul b/browser/components/onionservices/content/authPreferences.inc.xul
new file mode 100644
index 000000000000..0b6ce98efa31
--- /dev/null
+++ b/browser/components/onionservices/content/authPreferences.inc.xul
@@ -0,0 +1,20 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<groupbox id="torOnionServiceKeys" orient="vertical"
+ data-category="panePrivacy" hidden="true">
+ <label><html:h2 id="torOnionServiceKeys-header"/></label>
+ <hbox>
+ <description id="torOnionServiceKeys-overview-container" flex="1">
+ <html:span id="torOnionServiceKeys-overview"
+ class="tail-with-learn-more"/>
+ <label id="torOnionServiceKeys-learnMore" class="learnMore text-link"
+ is="text-link"/>
+ </description>
+ <vbox align="end">
+ <button id="torOnionServiceKeys-savedKeys"
+ is="highlightable-button"
+ class="accessory-button"
+ oncommand="OnionServicesAuthPreferences.onViewSavedKeys()"/>
+ </vbox>
+ </hbox>
+</groupbox>
diff --git a/browser/components/onionservices/content/authPreferences.js b/browser/components/onionservices/content/authPreferences.js
new file mode 100644
index 000000000000..c388fbee6b3e
--- /dev/null
+++ b/browser/components/onionservices/content/authPreferences.js
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+/*
+ Onion Services Client Authentication Preferences Code
+
+ Code to handle init and update of onion services authentication section
+ in about:preferences#privacy
+*/
+
+const OnionServicesAuthPreferences = {
+ selector: {
+ groupBox: "#torOnionServiceKeys",
+ header: "#torOnionServiceKeys-header",
+ overview: "#torOnionServiceKeys-overview",
+ learnMore: "#torOnionServiceKeys-learnMore",
+ savedKeysButton: "#torOnionServiceKeys-savedKeys",
+ },
+
+ init() {
+ // populate XUL with localized strings
+ this._populateXUL();
+ },
+
+ _populateXUL() {
+ const groupbox = document.querySelector(this.selector.groupBox);
+
+ let elem = groupbox.querySelector(this.selector.header);
+ elem.textContent = TorStrings.onionServices.authPreferences.header;
+
+ elem = groupbox.querySelector(this.selector.overview);
+ elem.textContent = TorStrings.onionServices.authPreferences.overview;
+
+ elem = groupbox.querySelector(this.selector.learnMore);
+ elem.setAttribute("value", TorStrings.onionServices.learnMore);
+ elem.setAttribute("href", TorStrings.onionServices.learnMoreURL);
+
+ elem = groupbox.querySelector(this.selector.savedKeysButton);
+ elem.setAttribute(
+ "label",
+ TorStrings.onionServices.authPreferences.savedKeys
+ );
+ },
+
+ onViewSavedKeys() {
+ gSubDialog.open(
+ "chrome://browser/content/onionservices/savedKeysDialog.xul"
+ );
+ },
+}; // OnionServicesAuthPreferences
+
+Object.defineProperty(this, "OnionServicesAuthPreferences", {
+ value: OnionServicesAuthPreferences,
+ enumerable: true,
+ writable: false,
+});
diff --git a/browser/components/onionservices/content/authPrompt.js b/browser/components/onionservices/content/authPrompt.js
index 2d4ebcafd688..f7a10e75158a 100644
--- a/browser/components/onionservices/content/authPrompt.js
+++ b/browser/components/onionservices/content/authPrompt.js
@@ -56,14 +56,14 @@ const OnionAuthPrompt = (function() {
};
this._prompt = PopupNotifications.show(this._browser,
- OnionAuthUtil.string.notificationID, "",
- OnionAuthUtil.string.anchorID,
+ OnionAuthUtil.domid.notification, "",
+ OnionAuthUtil.domid.anchor,
mainAction, [cancelAction], options);
},
_onPromptShowing(aWarningMessage) {
let xulDoc = this._browser.ownerDocument;
- let descElem = xulDoc.getElementById(OnionAuthUtil.string.descriptionID);
+ let descElem = xulDoc.getElementById(OnionAuthUtil.domid.description);
if (descElem) {
// Handle replacement of the onion name within the localized
// string ourselves so we can show the onion name as bold text.
@@ -89,7 +89,7 @@ const OnionAuthPrompt = (function() {
span.textContent = prefix;
descElem.appendChild(span);
span = xulDoc.createElementNS(kHTMLNS, "span");
- span.id = OnionAuthUtil.string.onionNameSpanID;
+ span.id = OnionAuthUtil.domid.onionNameSpan;
span.textContent = this._onionName;
descElem.appendChild(span);
span = xulDoc.createElementNS(kHTMLNS, "span");
@@ -98,13 +98,17 @@ const OnionAuthPrompt = (function() {
}
// Set "Learn More" label and href.
- let learnMoreElem = xulDoc.getElementById(OnionAuthUtil.string.learnMoreID);
+ let learnMoreElem = xulDoc.getElementById(OnionAuthUtil.domid.learnMore);
if (learnMoreElem) {
learnMoreElem.setAttribute("value", TorStrings.onionServices.learnMore);
learnMoreElem.setAttribute("href", TorStrings.onionServices.learnMoreURL);
}
this._showWarning(aWarningMessage);
+ let checkboxElem = this._getCheckboxElement();
+ if (checkboxElem) {
+ checkboxElem.checked = false;
+ }
},
_onPromptShown() {
@@ -170,7 +174,9 @@ const OnionAuthPrompt = (function() {
this.show(controllerFailureMsg);
});
let onionAddr = this._onionName.toLowerCase().replace(/\.onion$/, "");
- torController.onionAuthAdd(onionAddr, base64key)
+ let checkboxElem = this._getCheckboxElement();
+ let isPermanent = (checkboxElem && checkboxElem.checked);
+ torController.onionAuthAdd(onionAddr, base64key, isPermanent)
.then(aResponse => {
// Success! Reload the page.
this._browser.messageManager.sendAsyncMessage("Browser:Reload", {});
@@ -189,19 +195,24 @@ const OnionAuthPrompt = (function() {
_onCancel() {
// Arrange for an error page to be displayed.
this._browser.messageManager.sendAsyncMessage(
- OnionAuthUtil.string.authPromptCanceledMessage,
+ OnionAuthUtil.message.authPromptCanceled,
{failedURI: this._failedURI.spec});
},
_getKeyElement() {
let xulDoc = this._browser.ownerDocument;
- return xulDoc.getElementById(OnionAuthUtil.string.keyElementID);
+ return xulDoc.getElementById(OnionAuthUtil.domid.keyElement);
+ },
+
+ _getCheckboxElement() {
+ let xulDoc = this._browser.ownerDocument;
+ return xulDoc.getElementById(OnionAuthUtil.domid.checkboxElement);
},
_showWarning(aWarningMessage) {
let xulDoc = this._browser.ownerDocument;
let warningElem =
- xulDoc.getElementById(OnionAuthUtil.string.warningElementID);
+ xulDoc.getElementById(OnionAuthUtil.domid.warningElement);
let keyElem = this._getKeyElement();
if (warningElem) {
if (aWarningMessage) {
@@ -225,9 +236,12 @@ const OnionAuthPrompt = (function() {
let base64key;
if (aKeyString.length == 52) {
// The key is probably base32-encoded. Attempt to decode.
+ // Although base32 specifies uppercase letters, we accept lowercase
+ // as well because users may type in lowercase or copy a key out of
+ // a tor onion-auth file (which uses lowercase).
let rawKey;
try {
- rawKey = CommonUtils.decodeBase32(aKeyString);
+ rawKey = CommonUtils.decodeBase32(aKeyString.toUpperCase());
} catch (e) {}
if (rawKey) try {
@@ -247,17 +261,17 @@ const OnionAuthPrompt = (function() {
let retval = {
init() {
- Services.obs.addObserver(this, OnionAuthUtil.string.authPromptTopic);
+ Services.obs.addObserver(this, OnionAuthUtil.topic.authPrompt);
},
uninit() {
- Services.obs.removeObserver(this, OnionAuthUtil.string.authPromptTopic);
+ Services.obs.removeObserver(this, OnionAuthUtil.topic.authPrompt);
},
// aSubject is the DOM Window or browser where the prompt should be shown.
// aData contains the .onion name.
observe(aSubject, aTopic, aData) {
- if (aTopic != OnionAuthUtil.string.authPromptTopic) {
+ if (aTopic != OnionAuthUtil.topic.authPrompt) {
return;
}
diff --git a/browser/components/onionservices/content/authUtil.jsm b/browser/components/onionservices/content/authUtil.jsm
index 8547fba83a62..e9446f51cfcb 100644
--- a/browser/components/onionservices/content/authUtil.jsm
+++ b/browser/components/onionservices/content/authUtil.jsm
@@ -9,20 +9,25 @@ var EXPORTED_SYMBOLS = [
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const OnionAuthUtil = {
- string: {
- authPromptTopic: "tor-onion-services-auth-prompt",
- authPromptCanceledMessage: "Tor:OnionServicesAuthPromptCanceled",
- anchorID: "tor-clientauth-notification-icon",
- notificationID: "tor-clientauth",
- descriptionID: "tor-clientauth-notification-desc",
- learnMoreID: "tor-clientauth-notification-learnmore",
- onionNameSpanID: "tor-clientauth-notification-onionname",
- keyElementID: "tor-clientauth-notification-key",
- warningElementID: "tor-clientauth-warning",
+ topic: {
+ authPrompt: "tor-onion-services-auth-prompt",
+ },
+ message: {
+ authPromptCanceled: "Tor:OnionServicesAuthPromptCanceled",
+ },
+ domid: {
+ anchor: "tor-clientauth-notification-icon",
+ notification: "tor-clientauth",
+ description: "tor-clientauth-notification-desc",
+ learnMore: "tor-clientauth-notification-learnmore",
+ onionNameSpan: "tor-clientauth-notification-onionname",
+ keyElement: "tor-clientauth-notification-key",
+ warningElement: "tor-clientauth-warning",
+ checkboxElement: "tor-clientauth-persistkey-checkbox",
},
addCancelMessageListener(aTabContent, aDocShell) {
- aTabContent.addMessageListener(this.string.authPromptCanceledMessage,
+ aTabContent.addMessageListener(this.message.authPromptCanceled,
(aMessage) => {
let failedURI = Services.io.newURI(aMessage.data.failedURI);
aDocShell.displayLoadError(Cr.NS_ERROR_CONNECTION_REFUSED, failedURI,
diff --git a/browser/components/onionservices/content/savedKeysDialog.js b/browser/components/onionservices/content/savedKeysDialog.js
new file mode 100644
index 000000000000..b1376bbabe85
--- /dev/null
+++ b/browser/components/onionservices/content/savedKeysDialog.js
@@ -0,0 +1,259 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "controller",
+ "resource://torbutton/modules/tor-control-port.js"
+);
+
+var gOnionServicesSavedKeysDialog = {
+ selector: {
+ dialog: "#onionservices-savedkeys-dialog",
+ intro: "#onionservices-savedkeys-intro",
+ tree: "#onionservices-savedkeys-tree",
+ onionSiteCol: "#onionservices-savedkeys-siteCol",
+ onionKeyCol: "#onionservices-savedkeys-keyCol",
+ errorIcon: "#onionservices-savedkeys-errorIcon",
+ errorMessage: "#onionservices-savedkeys-errorMessage",
+ removeButton: "#onionservices-savedkeys-remove",
+ removeAllButton: "#onionservices-savedkeys-removeall",
+ },
+
+ _tree: undefined,
+ _isBusy: false, // true when loading data, deleting a key, etc.
+
+ // Public functions (called from outside this file).
+ async deleteSelectedKeys() {
+ this._setBusyState(true);
+
+ const indexesToDelete = [];
+ const count = this._tree.view.selection.getRangeCount();
+ for (let i = 0; i < count; ++i) {
+ const minObj = {};
+ const maxObj = {};
+ this._tree.view.selection.getRangeAt(i, minObj, maxObj);
+ for (let idx = minObj.value; idx <= maxObj.value; ++idx) {
+ indexesToDelete.push(idx);
+ }
+ }
+
+ if (indexesToDelete.length > 0) {
+ const controllerFailureMsg =
+ TorStrings.onionServices.authPreferences.failedToRemoveKey;
+ try {
+ const torController = controller(aError => {
+ this._showError(controllerFailureMsg);
+ });
+
+ // Remove in reverse index order to avoid issues caused by index changes.
+ for (let i = indexesToDelete.length - 1; i >= 0; --i) {
+ await this._deleteOneKey(torController, indexesToDelete[i]);
+ }
+ } catch (e) {
+ if (e.torMessage) {
+ this._showError(e.torMessage);
+ } else {
+ this._showError(controllerFailureMsg);
+ }
+ }
+ }
+
+ this._setBusyState(false);
+ },
+
+ async deleteAllKeys() {
+ this._tree.view.selection.selectAll();
+ await this.deleteSelectedKeys();
+ },
+
+ updateButtonsState() {
+ const haveSelection = this._tree.view.selection.getRangeCount() > 0;
+ const dialog = document.querySelector(this.selector.dialog);
+ const removeSelectedBtn = dialog.querySelector(this.selector.removeButton);
+ removeSelectedBtn.disabled = this._isBusy || !haveSelection;
+ const removeAllBtn = dialog.querySelector(this.selector.removeAllButton);
+ removeAllBtn.disabled = this._isBusy || this.rowCount === 0;
+ },
+
+ // Private functions.
+ _onLoad() {
+ document.mozSubdialogReady = this._init();
+ },
+
+ async _init() {
+ await this._populateXUL();
+
+ window.addEventListener("keypress", this._onWindowKeyPress.bind(this));
+
+ // We don't use await here because we want _loadSavedKeys() to run
+ // in the background and not block loading of this dialog.
+ this._loadSavedKeys();
+ },
+
+ async _populateXUL() {
+ const dialog = document.querySelector(this.selector.dialog);
+ const authPrefStrings = TorStrings.onionServices.authPreferences;
+ dialog.setAttribute("title", authPrefStrings.dialogTitle);
+
+ let elem = dialog.querySelector(this.selector.intro);
+ elem.textContent = authPrefStrings.dialogIntro;
+
+ elem = dialog.querySelector(this.selector.onionSiteCol);
+ elem.setAttribute("label", authPrefStrings.onionSite);
+
+ elem = dialog.querySelector(this.selector.onionKeyCol);
+ elem.setAttribute("label", authPrefStrings.onionKey);
+
+ elem = dialog.querySelector(this.selector.removeButton);
+ elem.setAttribute("label", authPrefStrings.remove);
+
+ elem = dialog.querySelector(this.selector.removeAllButton);
+ elem.setAttribute("label", authPrefStrings.removeAll);
+
+ this._tree = dialog.querySelector(this.selector.tree);
+ },
+
+ async _loadSavedKeys() {
+ const controllerFailureMsg =
+ TorStrings.onionServices.authPreferences.failedToGetKeys;
+ this._setBusyState(true);
+
+ try {
+ this._tree.view = this;
+
+ const torController = controller(aError => {
+ this._showError(controllerFailureMsg);
+ });
+
+ const keyInfoList = await torController.onionAuthViewKeys();
+ if (keyInfoList) {
+ // Filter out temporary keys.
+ this._keyInfoList = keyInfoList.filter(aKeyInfo => {
+ if (!aKeyInfo.Flags) {
+ return false;
+ }
+
+ const flags = aKeyInfo.Flags.split(",");
+ return flags.includes("Permanent");
+ });
+
+ // Sort by the .onion address.
+ this._keyInfoList.sort((aObj1, aObj2) => {
+ const hsAddr1 = aObj1.hsAddress.toLowerCase();
+ const hsAddr2 = aObj2.hsAddress.toLowerCase();
+ if (hsAddr1 < hsAddr2) {
+ return -1;
+ }
+ return hsAddr1 > hsAddr2 ? 1 : 0;
+ });
+ }
+
+ // Render the tree content.
+ this._tree.rowCountChanged(0, this.rowCount);
+ } catch (e) {
+ if (e.torMessage) {
+ this._showError(e.torMessage);
+ } else {
+ this._showError(controllerFailureMsg);
+ }
+ }
+
+ this._setBusyState(false);
+ },
+
+ // This method may throw; callers should catch errors.
+ async _deleteOneKey(aTorController, aIndex) {
+ const keyInfoObj = this._keyInfoList[aIndex];
+ await aTorController.onionAuthRemove(keyInfoObj.hsAddress);
+ this._tree.view.selection.clearRange(aIndex, aIndex);
+ this._keyInfoList.splice(aIndex, 1);
+ this._tree.rowCountChanged(aIndex + 1, -1);
+ },
+
+ _setBusyState(aIsBusy) {
+ this._isBusy = aIsBusy;
+ this.updateButtonsState();
+ },
+
+ _onWindowKeyPress(event) {
+ if (event.keyCode === KeyEvent.DOM_VK_ESCAPE) {
+ window.close();
+ } else if (event.keyCode === KeyEvent.DOM_VK_DELETE) {
+ this.deleteSelectedKeys();
+ }
+ },
+
+ _showError(aMessage) {
+ const dialog = document.querySelector(this.selector.dialog);
+ const errorIcon = dialog.querySelector(this.selector.errorIcon);
+ errorIcon.style.visibility = aMessage ? "visible" : "hidden";
+ const errorDesc = dialog.querySelector(this.selector.errorMessage);
+ errorDesc.textContent = aMessage ? aMessage : "";
+ },
+
+ // XUL tree widget view implementation.
+ get rowCount() {
+ return this._keyInfoList ? this._keyInfoList.length : 0;
+ },
+
+ getCellText(aRow, aCol) {
+ let val = "";
+ if (this._keyInfoList && aRow < this._keyInfoList.length) {
+ const keyInfo = this._keyInfoList[aRow];
+ if (aCol.id.endsWith("-siteCol")) {
+ val = keyInfo.hsAddress;
+ } else if (aCol.id.endsWith("-keyCol")) {
+ val = keyInfo.typeAndKey;
+ // Omit keyType because it is always "x25519".
+ const idx = val.indexOf(":");
+ if (idx > 0) {
+ val = val.substring(idx + 1);
+ }
+ }
+ }
+
+ return val;
+ },
+
+ isSeparator(index) {
+ return false;
+ },
+
+ isSorted() {
+ return false;
+ },
+
+ isContainer(index) {
+ return false;
+ },
+
+ setTree(tree) {},
+
+ getImageSrc(row, column) {},
+
+ getCellValue(row, column) {},
+
+ cycleHeader(column) {},
+
+ getRowProperties(row) {
+ return "";
+ },
+
+ getColumnProperties(column) {
+ return "";
+ },
+
+ getCellProperties(row, column) {
+ return "";
+ },
+};
+
+window.addEventListener("load", () => gOnionServicesSavedKeysDialog._onLoad());
diff --git a/browser/components/onionservices/content/savedKeysDialog.xul b/browser/components/onionservices/content/savedKeysDialog.xul
new file mode 100644
index 000000000000..3db9bb05ea82
--- /dev/null
+++ b/browser/components/onionservices/content/savedKeysDialog.xul
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!-- Copyright (c) 2020, The Tor Project, Inc. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/onionservices/authPreferences.css" type="text/css"?>
+
+<window id="onionservices-savedkeys-dialog"
+ windowtype="OnionServices:SavedKeys"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 45em;">
+
+ <script src="chrome://browser/content/onionservices/savedKeysDialog.js"/>
+
+ <vbox id="onionservices-savedkeys" class="contentPane" flex="1">
+ <label id="onionservices-savedkeys-intro"
+ control="onionservices-savedkeys-tree"/>
+ <separator class="thin"/>
+ <tree id="onionservices-savedkeys-tree" flex="1" hidecolumnpicker="true"
+ width="750"
+ style="height: 20em;"
+ onselect="gOnionServicesSavedKeysDialog.updateButtonsState();">
+ <treecols>
+ <treecol id="onionservices-savedkeys-siteCol" flex="1" persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="onionservices-savedkeys-keyCol" flex="1" persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox id="onionservices-savedkeys-errorContainer" align="baseline" flex="1">
+ <image id="onionservices-savedkeys-errorIcon"/>
+ <description id="onionservices-savedkeys-errorMessage" flex="1"/>
+ </hbox>
+ <separator class="thin"/>
+ <hbox id="onionservices-savedkeys-buttons">
+ <button id="onionservices-savedkeys-remove" disabled="true"
+ oncommand="gOnionServicesSavedKeysDialog.deleteSelectedKeys();"/>
+ <button id="onionservices-savedkeys-removeall"
+ oncommand="gOnionServicesSavedKeysDialog.deleteAllKeys();"/>
+ </hbox>
+ </vbox>
+</window>
diff --git a/browser/components/onionservices/jar.mn b/browser/components/onionservices/jar.mn
index 06cf2df6e7ac..583ab77bc6d8 100644
--- a/browser/components/onionservices/jar.mn
+++ b/browser/components/onionservices/jar.mn
@@ -1,4 +1,8 @@
browser.jar:
+ content/browser/onionservices/authPreferences.css (content/authPreferences.css)
+ content/browser/onionservices/authPreferences.js (content/authPreferences.js)
content/browser/onionservices/authPrompt.js (content/authPrompt.js)
content/browser/onionservices/authUtil.jsm (content/authUtil.jsm)
content/browser/onionservices/onionservices.css (content/onionservices.css)
+ content/browser/onionservices/savedKeysDialog.js (content/savedKeysDialog.js)
+ content/browser/onionservices/savedKeysDialog.xul (content/savedKeysDialog.xul)
diff --git a/browser/components/preferences/in-content/preferences.xul b/browser/components/preferences/in-content/preferences.xul
index 7a01443ab048..30915e3d358f 100644
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -15,6 +15,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/privacy.css"?>
+<?xml-stylesheet href="chrome://browser/content/onionservices/authPreferences.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js
index 297d07fadf1f..ee86b4158d7c 100644
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -62,6 +62,12 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["OnionServicesAuthPreferences"],
+ "chrome://browser/content/onionservices/authPreferences.js"
+);
+
// TODO: module import via ChromeUtils.defineModuleGetter
XPCOMUtils.defineLazyScriptGetter(
this,
@@ -369,6 +375,7 @@ var gPrivacyPane = {
this.trackingProtectionReadPrefs();
this.networkCookieBehaviorReadPrefs();
this._initTrackingProtectionExtensionControl();
+ OnionServicesAuthPreferences.init();
this._initSecurityLevel();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul
index 013fe147bc82..e807ac69f1f1 100644
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -468,6 +468,8 @@
</hbox>
</groupbox>
+#include ../../onionservices/content/authPreferences.inc.xul
+
<!-- The form autofill section is inserted in to this box
after the form autofill extension has initialized. -->
<groupbox id="formAutofillGroupBox"
diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm
index f68d60cf1343..e9a8b3969297 100644
--- a/browser/modules/TorStrings.jsm
+++ b/browser/modules/TorStrings.jsm
@@ -329,7 +329,7 @@ var TorStrings = {
};
let retval = {
- learnMore: getString("torPreferences.learnMore", "Learn More"),
+ learnMore: getString("learnMore", "Learn more"),
learnMoreURL: `https://2019.www.torproject.org/docs/tor-manual-dev.html.${getLocale()}#_client_authorization`,
authPrompt: {
description:
@@ -341,6 +341,19 @@ var TorStrings = {
failedToSetKey:
getString("authPrompt.failedToSetKey", "Failed to set key"),
},
+ authPreferences: {
+ header: getString("authPreferences.header", "Onion Services Authentication"),
+ overview: getString("authPreferences.overview", "Some onion services require that you identify yourself with a key"),
+ savedKeys: getString("authPreferences.savedKeys", "Saved Keys"),
+ dialogTitle: getString("authPreferences.dialogTitle", "Onion Services Keys"),
+ dialogIntro: getString("authPreferences.dialogIntro", "Keys for the following onionsites are stored on your computer"),
+ onionSite: getString("authPreferences.onionSite", "Onionsite"),
+ onionKey: getString("authPreferences.onionKey", "Key"),
+ remove: getString("authPreferences.remove", "Remove"),
+ removeAll: getString("authPreferences.removeAll", "Remove All"),
+ failedToGetKeys: getString("authPreferences.failedToGetKeys", "Failed to get keys"),
+ failedToRemoveKey: getString("authPreferences.failedToRemoveKey", "Failed to remove key"),
+ },
};
return retval;
More information about the tbb-commits
mailing list