commit aed69dc95387429e18b18ad578fb78d4a83d91f2 Author: Kathy Brade brade@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.$%7BgetLocale()%7D#..., 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;
tbb-commits@lists.torproject.org