From f9723a6183647b3453600751bf91eb51b0788c04 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 24 Oct 2019 16:06:33 +0200 Subject: [PATCH] Fix read-only shared folders with password change --- www/common/cryptpad-common.js | 29 ++++++++++++++++------- www/common/outer/sharedfolder.js | 29 ++++++++++++++--------- www/common/outer/userObject.js | 11 ++++++--- www/common/proxy-manager.js | 10 +++++++- www/common/userObject.js | 40 ++++++++++++++++++++++++++++++-- 5 files changed, 93 insertions(+), 26 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index dab5fc89d..9ed46ce04 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -6,6 +6,7 @@ define([ '/common/common-messaging.js', '/common/common-constants.js', '/common/common-feedback.js', + '/common/userObject.js', '/common/outer/local-store.js', '/common/outer/worker-channel.js', '/common/outer/login-block.js', @@ -13,7 +14,7 @@ define([ '/customize/application_config.js', '/bower_components/nthen/index.js', ], function (Config, Messages, Util, Hash, - Messaging, Constants, Feedback, LocalStore, Channel, Block, + Messaging, Constants, Feedback, UserObject, LocalStore, Channel, Block, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -158,7 +159,7 @@ define([ }); }; common.addSharedFolder = function (teamId, secret, cb) { - var href = secret.keys && secret.keys.editKeyStr ? '/drive/#' + Hash.getEditHashFromKeys(secret) : undefined; + var href = (secret.keys && secret.keys.editKeyStr) ? '/drive/#' + Hash.getEditHashFromKeys(secret) : undefined; postMessage("ADD_SHARED_FOLDER", { teamId: teamId, path: ['root'], @@ -878,6 +879,8 @@ define([ initialState: isSharedFolder ? '{}' : undefined }; + var cryptgetVal; + Nthen(function (waitFor) { if (parsed.hashData && parsed.hashData.password) { common.getPadAttribute('password', waitFor(function (err, password) { @@ -946,13 +949,22 @@ define([ waitFor.abort(); return void cb({ error: err }); } - Crypt.put(newHash, val, waitFor(function (err) { - if (err) { - waitFor.abort(); - return void cb({ error: err }); - } - }), optsPut); + cryptgetVal = val; + if (isSharedFolder) { + var parsed = JSON.parse(val || '{}'); + var oldKey = parsed.version === 2 && oldSecret.keys.secondaryKey; + var newKey = newSecret.keys.secondaryKey; + UserObject.reencrypt(oldKey, newKey, parsed); + cryptgetVal = JSON.stringify(parsed); + } }), optsGet); + }).nThen(function (waitFor) { + Crypt.put(newHash, cryptgetVal, waitFor(function (err) { + if (err) { + waitFor.abort(); + return void cb({ error: err }); + } + }), optsPut); }).nThen(function (waitFor) { if (isSharedFolder) { postMessage("UPDATE_SHARED_FOLDER_PASSWORD", { @@ -1514,7 +1526,6 @@ define([ noWorker = localStorage.CryptPad_noWorkers === '1'; console.error('WebWorker/SharedWorker state forced to ' + !noWorker); } - noWorker = true; Nthen(function (waitFor2) { if (Worker) { var w = waitFor2(); diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index 2ebb718ec..a118e04e3 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -116,6 +116,7 @@ define([ sf.teams.push({ cb: cb, store: store, + secondaryKey: secondaryKey, id: id }); if (handler) { handler(id, sf.rt); } @@ -126,16 +127,17 @@ define([ teams: [{ cb: cb, store: store, + secondaryKey: secondaryKey, id: id }], - readOnly: Boolean(secondaryKey) + readOnly: !Boolean(secondaryKey) }; var owners = data.owners; var listmapConfig = { data: {}, channel: secret.channel, - readOnly: Boolean(secondaryKey), + readOnly: !Boolean(secondaryKey), crypto: Crypto.createEncryptor(secret.keys), userName: 'sharedFolder', logLevel: 1, @@ -158,7 +160,7 @@ define([ } sf.teams.forEach(function (obj) { var leave = function () { SF.leave(secret.channel, teamId); }; - var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey); + var uo = obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey); SF.checkMigration(secondaryKey, rt.proxy, uo, function () { obj.cb(sf.rt, info.metadata); }); @@ -171,10 +173,8 @@ define([ if (info.error === "EDELETED" ) { try { // Deprecate the shared folder from each team - // XXX We can't deprecate a read-only proxy: the read-only seed will change... - // We can only remove it + // We can only hide it sf.teams.forEach(function (obj) { - console.log(obj.store.id, obj.store, obj.id); obj.store.manager.deprecateProxy(obj.id, secret.channel); }); } catch (e) {} @@ -187,6 +187,7 @@ define([ }); }; + SF.upgrade = function (channel, secret) { var sf = allSharedFolders[channel]; if (!sf || !sf.readOnly) { return; } @@ -235,17 +236,21 @@ define([ } var nt = nThen; sf.teams.forEach(function (obj) { - // XXX if we're a viewer in this team, we can't update the keys nt = nt(function (waitFor) { var s = obj.store; var sfId = obj.id; + // We can't update the password of a shared folder in a read-only team + if (s.manager.user.userObject.readOnly) { + // Just deprecate the folder so that inner can stop displaying a folder no longer available + if (s.manager.folders[sfId]) { + s.manager.folders[sfId].proxy = { deprecated: true }; + } + return; + } var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; if (!sfId || !shared[sfId]) { return; } var sf = JSON.parse(JSON.stringify(shared[sfId])); sf.password = password; - sf.channel = secret.channel; - sf.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt - sf.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret); SF.load({ network: network, store: s, @@ -256,7 +261,9 @@ define([ s.rpc.pin([secret.channel], waitFor()); }).nThen; }); - nt(cb); + nt(function () { + cb(); + }); }; /* loadSharedFolders diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js index 8cdbae7b4..55fe8014d 100644 --- a/www/common/outer/userObject.js +++ b/www/common/outer/userObject.js @@ -71,19 +71,21 @@ define([ cb(null, clone(data[attr])); }; - exp.pushData = function (data, cb) { + exp.pushData = function (_data, cb) { if (typeof cb !== "function") { cb = function () {}; } if (readOnly) { return void cb('EFORBIDDEN'); } var id = Util.createRandomInteger(); + var data = clone(_data); // If we were given an edit link, encrypt its value if needed if (data.href) { data.href = exp.cryptor.encrypt(data.href); } files[FILES_DATA][id] = data; cb(null, id); }; - exp.pushSharedFolder = function (data, cb) { + exp.pushSharedFolder = function (_data, cb) { if (typeof cb !== "function") { cb = function () {}; } if (readOnly) { return void cb('EFORBIDDEN'); } + var data = clone(_data); // Check if we already have this shared folder in our drive var exists; @@ -476,6 +478,9 @@ define([ files.migrateRo = 1; var next = function () { var copy = JSON.parse(JSON.stringify(files)); + exp.reencrypt(null, config.editKey, copy); + // XXX test migration again + /* Object.keys(copy[FILES_DATA]).forEach(function (id) { var data = copy[FILES_DATA][id] || {}; // If this pad has a visible href, encrypt it @@ -490,7 +495,7 @@ define([ if (data.href && data.roHref && !data.fileType && data.href.indexOf('#') !== -1) { data.href = exp.cryptor.encrypt(data.href); } - }); + });*/ copy.version = 2; delete copy.migrateRo; diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 788c59fae..bcbd240fb 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -52,6 +52,13 @@ define([ // Password may have changed var deprecateProxy = function (Env, id, channel) { + if (Env.user.userObject.readOnly) { + // In a read-only team, we can't deprecate a shared folder + if (Env.folders[id]) { + Env.folders[id].proxy = { deprecated: true }; + } + return void Env.Store.refreshDriveUI(); + } Env.unpinPads([channel], function () {}); Env.user.userObject.deprecateSharedFolder(id); if (Env.Store && Env.Store.refreshDriveUI) { @@ -554,7 +561,8 @@ define([ var secret = Hash.getSecrets(parsed.type, parsed.hash, newPassword); data.password = newPassword; data.channel = secret.channel; - data.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt + var _href = '/drive/#'+Hash.getEditHashFromKeys(secret); + data.href = Env.user.userObject.cryptor.encrypt(_href); data.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret); _addSharedFolder(Env, { path: ['root'], diff --git a/www/common/userObject.js b/www/common/userObject.js index bdfff2caa..74ed702d7 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -15,6 +15,8 @@ define([ var TEMPLATE = module.TEMPLATE = "template"; var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders"; var SHARED_FOLDERS_TEMP = module.SHARED_FOLDERS_TEMP = "sharedFoldersTemp"; // Maybe deleted or new password + var FILES_DATA = module.FILES_DATA = Constants.storageKey; + var OLD_FILES_DATA = module.OLD_FILES_DATA = Constants.oldStorageKey; // Create untitled documents when no name is given var getLocaleDate = function () { @@ -66,6 +68,37 @@ define([ return pad.roHref; }; + module.reencrypt = function (oldKey, newKey, obj) { + obj = obj || {}; + var oldCryptor = createCryptor(oldKey); + var newCryptor = createCryptor(newKey); + Object.keys(obj[FILES_DATA]).forEach(function (id) { + var data = obj[FILES_DATA][id] || {}; + // If this pad has a visible href, encrypt it + // "&& data.roHref" is here to make sure this is not a "file" + if (data.href && data.roHref && !data.fileType) { + var _href = oldCryptor.decrypt(data.href); + data.href = newCryptor.encrypt(_href); + } + }); + Object.keys(obj[SHARED_FOLDERS] || {}).forEach(function (id) { + var data = obj[SHARED_FOLDERS][id] || {}; + // If this folder has a visible href, encrypt it + if (data.href) { + var _href = oldCryptor.decrypt(data.href); + data.href = newCryptor.encrypt(_href); + } + }); + Object.keys(obj[SHARED_FOLDERS_TEMP] || {}).forEach(function (id) { + var data = obj[SHARED_FOLDERS_TEMP][id] || {}; + // If this folder has a visible href, encrypt it + if (data.href) { + var _href = oldCryptor.decrypt(data.href); + data.href = newCryptor.encrypt(_href); + } + }); + }; + module.init = function (files, config) { var exp = {}; @@ -74,18 +107,19 @@ define([ exp.setReadOnly = function (state, key) { config.editKey = key; createCryptor(key); + exp.readOnly = state; if (exp._setReadOnly) { // Change outer exp._setReadOnly(state); } }; + exp.readOnly = config.readOnly; + exp.reencrypt = module.reencrypt; exp.getDefaultName = module.getDefaultName; var sframeChan = config.sframeChan; - var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey; - var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Constants.oldStorageKey; var NEW_FOLDER_NAME = Messages.fm_newFolder || 'New folder'; var NEW_FILE_NAME = Messages.fm_newFile || 'New file'; @@ -95,6 +129,8 @@ define([ exp.TEMPLATE = TEMPLATE; exp.SHARED_FOLDERS = SHARED_FOLDERS; exp.SHARED_FOLDERS_TEMP = SHARED_FOLDERS_TEMP; + exp.FILES_DATA = FILES_DATA; + exp.OLD_FILES_DATA = OLD_FILES_DATA; var sharedFolder = exp.sharedFolder = config.sharedFolder; exp.id = config.id;