From d189db2bda8e7335fa70fe749165bbf26272bcb3 Mon Sep 17 00:00:00 2001 From: Jun Futagawa Date: Wed, 6 Mar 2019 01:20:55 +0900 Subject: [PATCH] Add random password generation support for each upload The generated password is printed at the beginning of the message body. Also, if it disable the random password option, the fixed password is printed at the beginning of the message body. Fixes: #54 Signed-off-by: Jun Futagawa --- src/chrome/content/settings.js | 16 +++++- src/chrome/content/settings.xhtml | 3 ++ src/chrome/locale/de/settings.dtd | 1 + src/chrome/locale/en/settings.dtd | 1 + src/chrome/locale/es/settings.dtd | 1 + src/chrome/locale/fr/settings.dtd | 1 + src/chrome/locale/nl/settings.dtd | 1 + src/chrome/locale/pl/settings.dtd | 1 + src/components/nsNextcloud.js | 89 ++++++++++++++++++++++++++++++- 9 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/chrome/content/settings.js b/src/chrome/content/settings.js index d354f6b..00e2061 100644 --- a/src/chrome/content/settings.js +++ b/src/chrome/content/settings.js @@ -28,7 +28,7 @@ * string, value: *}, port: {type: string, value: (string|string|string|Number)}, * storageFolder: {type: string, value: (string|string|string|Number)}, username: {type: * string, value: (string|string|string|Number)}, protectUploads: {type: string, value: - * (string|string|string|Number)}}} + * (string|string|string|Number), useRandomPassword: {type: bool, value: bool}}} */ function extraArgs () { let displayName = document.getElementById("displayName").value; @@ -37,6 +37,7 @@ function extraArgs () { let storageFolderValue = document.getElementById("storageFolder").value; let userValue = document.getElementById("username").value; let protectUploadsValue = document.getElementById("protectUploads").value; + let useRandomPasswordValue = document.getElementById("useRandomPassword").checked; return { "displayName": { @@ -62,6 +63,19 @@ function extraArgs () { "protectUploads": { type: "char", value: protectUploadsValue + }, + "useRandomPassword": { + type: "bool", + value: useRandomPasswordValue } }; } + +function onUseRandomPasswordClick () { + let useRandomPassword = document.getElementById("useRandomPassword").checked; + if (useRandomPassword) { + document.getElementById("protectUploads").disabled = "disabled"; + } else { + document.getElementById("protectUploads").disabled = ""; + } +} diff --git a/src/chrome/content/settings.xhtml b/src/chrome/content/settings.xhtml index 9cf0926..f3e2b90 100644 --- a/src/chrome/content/settings.xhtml +++ b/src/chrome/content/settings.xhtml @@ -50,6 +50,9 @@ +
&nextcloudSettings.learnMore; diff --git a/src/chrome/locale/de/settings.dtd b/src/chrome/locale/de/settings.dtd index 155f3f4..0716e2c 100644 --- a/src/chrome/locale/de/settings.dtd +++ b/src/chrome/locale/de/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/en/settings.dtd b/src/chrome/locale/en/settings.dtd index 97f1a83..db641c5 100644 --- a/src/chrome/locale/en/settings.dtd +++ b/src/chrome/locale/en/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/es/settings.dtd b/src/chrome/locale/es/settings.dtd index 37b1fb0..af19c8e 100644 --- a/src/chrome/locale/es/settings.dtd +++ b/src/chrome/locale/es/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/fr/settings.dtd b/src/chrome/locale/fr/settings.dtd index c476944..0557d43 100644 --- a/src/chrome/locale/fr/settings.dtd +++ b/src/chrome/locale/fr/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/nl/settings.dtd b/src/chrome/locale/nl/settings.dtd index c7deb62..dcfae27 100644 --- a/src/chrome/locale/nl/settings.dtd +++ b/src/chrome/locale/nl/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/chrome/locale/pl/settings.dtd b/src/chrome/locale/pl/settings.dtd index 2f4ad48..a492e0f 100644 --- a/src/chrome/locale/pl/settings.dtd +++ b/src/chrome/locale/pl/settings.dtd @@ -30,3 +30,4 @@ + diff --git a/src/components/nsNextcloud.js b/src/components/nsNextcloud.js index f20ab9a..9e09d9e 100644 --- a/src/components/nsNextcloud.js +++ b/src/components/nsNextcloud.js @@ -114,6 +114,7 @@ Nextcloud.prototype = { _userName: "", _password: "", _protectUploads: "", + _useRandomPassword: false, _prefBranch: null, _loggedIn: false, _authToken: "", @@ -133,6 +134,7 @@ Nextcloud.prototype = { _uploads: [], _urlsForFiles: {}, _uploadInfo: {}, // upload info keyed on aFiles. + _messageWindow: null, /** * Initialize this instance of Nextcloud, setting the accountKey. @@ -168,6 +170,14 @@ Nextcloud.prototype = { if (this._prefBranch.prefHasUserValue("protectUploads")) { this._protectUploads = this._prefBranch.getCharPref("protectUploads"); } + + if (this._prefBranch.prefHasUserValue("useRandomPassword")) { + this._useRandomPassword = this._prefBranch.getBoolPref("useRandomPassword"); + } + + let windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + this._messageWindow = windowMediator.getMostRecentWindow("msgcompose"); }, /** @@ -237,6 +247,32 @@ Nextcloud.prototype = { * @param aFile the nsILocalFile to retrieve the URL for */ urlForFile: function nsNc_urlForFile (aFile) { + if (this._uploadInfo["downloadPassword"] == null) { + return this._urlsForFiles[aFile.path]; + } + + // Output download password + let document = this._messageWindow.document; + let contentFrame = document.getElementById("content-frame"); + let contentDocument = contentFrame.contentDocument; + this.log.debug("Document body: " + contentDocument.body.innerHTML); + + let cloudAttachmentPasswordList = contentDocument.getElementById("cloudAttachmentPasswordList"); + if (cloudAttachmentPasswordList == null) { + // First cloud attachment + contentDocument.body.insertAdjacentHTML("afterbegin", + '
'); + cloudAttachmentPasswordList = contentDocument.getElementById("cloudAttachmentPasswordList"); + } + cloudAttachmentPasswordList.insertAdjacentHTML("beforeend", + '
* Download password for ' + + aFile.leafName + ":
" + + this._uploadInfo["downloadPassword"] + '
'); + + if (contentFrame.editortype == "textmail") { + // Start a new line before url + return "\n" + this._urlsForFiles[aFile.path]; + } return this._urlsForFiles[aFile.path]; }, @@ -866,8 +902,15 @@ NextcloudFileUploader.prototype = { let formData = "shareType=" + shareType + "&path=" + path; // Request a password for the link if it has been defined during setup time - if (this.nextcloud._protectUploads.length) { - formData += "&password=" + wwwFormUrlEncode(this.nextcloud._protectUploads); + let downloadPassword = this.nextcloud._protectUploads; + // Use random password for each upload + if (this.nextcloud._useRandomPassword) { + downloadPassword = this._generatePassword(16); + } + if (downloadPassword.length) { + this.log.debug("FormData password: " + downloadPassword); + this.nextcloud._uploadInfo["downloadPassword"] = downloadPassword; + formData += "&password=" + wwwFormUrlEncode(downloadPassword); } req.open("POST", @@ -918,6 +961,48 @@ NextcloudFileUploader.prototype = { }.bind(this); this.log.debug("Raw formData: " + formData); req.send(formData); + }, + + /** + * A private function which generates a password. + * + * On Nextcloud, most strict password policy require: + * - Enforce upper and lower case characters + * - Enforce numeric characters + * - Enforce special characters + * + * @param length password length + * @private + */ + _generatePassword: function generatePassword(length) { + const lower = "abcdefghijklmnopqrstuvwxyz"; + const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const numeric = "0123456789" + // Excludes characters that fail to output if continuous: <> + const special = "!\"#$%&'()*+,-./:;=?@[\\]^_`{|}~"; + const seed = lower + upper + numeric + special; + + const lowerRegex = new RegExp("[" + lower + "]"); + const upperRegex = new RegExp("[" + upper + "]"); + const numericRegex = new RegExp("[" + numeric + "]"); + const specialRegex = new RegExp("[" + special + "]"); + + let limit = 100000; + let i = 0; + let password = ""; + while (i < limit) { + i++; + password = Array.from(Array(length)).map(() => seed[Math.floor(Math.random() * seed.length)]).join(""); + + if (!lowerRegex.test(password)) continue; + if (!upperRegex.test(password)) continue; + if (!numericRegex.test(password)) continue; + if (!specialRegex.test(password)) continue; + + break; + } + this.log.debug("Generated password: " + i + ": " + password); + return password; } };