Fix read-only shared folders with password change

pull/1/head
yflory 5 years ago
parent 0583ed3c8b
commit f9723a6183

@ -6,6 +6,7 @@ define([
'/common/common-messaging.js', '/common/common-messaging.js',
'/common/common-constants.js', '/common/common-constants.js',
'/common/common-feedback.js', '/common/common-feedback.js',
'/common/userObject.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/outer/worker-channel.js', '/common/outer/worker-channel.js',
'/common/outer/login-block.js', '/common/outer/login-block.js',
@ -13,7 +14,7 @@ define([
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash, ], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, LocalStore, Channel, Block, Messaging, Constants, Feedback, UserObject, LocalStore, Channel, Block,
AppConfig, Nthen) { AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
@ -158,7 +159,7 @@ define([
}); });
}; };
common.addSharedFolder = function (teamId, secret, cb) { 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", { postMessage("ADD_SHARED_FOLDER", {
teamId: teamId, teamId: teamId,
path: ['root'], path: ['root'],
@ -878,6 +879,8 @@ define([
initialState: isSharedFolder ? '{}' : undefined initialState: isSharedFolder ? '{}' : undefined
}; };
var cryptgetVal;
Nthen(function (waitFor) { Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) { if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) { common.getPadAttribute('password', waitFor(function (err, password) {
@ -946,13 +949,22 @@ define([
waitFor.abort(); waitFor.abort();
return void cb({ error: err }); return void cb({ error: err });
} }
Crypt.put(newHash, val, waitFor(function (err) { 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) { if (err) {
waitFor.abort(); waitFor.abort();
return void cb({ error: err }); return void cb({ error: err });
} }
}), optsPut); }), optsPut);
}), optsGet);
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
if (isSharedFolder) { if (isSharedFolder) {
postMessage("UPDATE_SHARED_FOLDER_PASSWORD", { postMessage("UPDATE_SHARED_FOLDER_PASSWORD", {
@ -1514,7 +1526,6 @@ define([
noWorker = localStorage.CryptPad_noWorkers === '1'; noWorker = localStorage.CryptPad_noWorkers === '1';
console.error('WebWorker/SharedWorker state forced to ' + !noWorker); console.error('WebWorker/SharedWorker state forced to ' + !noWorker);
} }
noWorker = true;
Nthen(function (waitFor2) { Nthen(function (waitFor2) {
if (Worker) { if (Worker) {
var w = waitFor2(); var w = waitFor2();

@ -116,6 +116,7 @@ define([
sf.teams.push({ sf.teams.push({
cb: cb, cb: cb,
store: store, store: store,
secondaryKey: secondaryKey,
id: id id: id
}); });
if (handler) { handler(id, sf.rt); } if (handler) { handler(id, sf.rt); }
@ -126,16 +127,17 @@ define([
teams: [{ teams: [{
cb: cb, cb: cb,
store: store, store: store,
secondaryKey: secondaryKey,
id: id id: id
}], }],
readOnly: Boolean(secondaryKey) readOnly: !Boolean(secondaryKey)
}; };
var owners = data.owners; var owners = data.owners;
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
channel: secret.channel, channel: secret.channel,
readOnly: Boolean(secondaryKey), readOnly: !Boolean(secondaryKey),
crypto: Crypto.createEncryptor(secret.keys), crypto: Crypto.createEncryptor(secret.keys),
userName: 'sharedFolder', userName: 'sharedFolder',
logLevel: 1, logLevel: 1,
@ -158,7 +160,7 @@ define([
} }
sf.teams.forEach(function (obj) { sf.teams.forEach(function (obj) {
var leave = function () { SF.leave(secret.channel, teamId); }; 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 () { SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
obj.cb(sf.rt, info.metadata); obj.cb(sf.rt, info.metadata);
}); });
@ -171,10 +173,8 @@ define([
if (info.error === "EDELETED" ) { if (info.error === "EDELETED" ) {
try { try {
// Deprecate the shared folder from each team // 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 hide it
// We can only remove it
sf.teams.forEach(function (obj) { sf.teams.forEach(function (obj) {
console.log(obj.store.id, obj.store, obj.id);
obj.store.manager.deprecateProxy(obj.id, secret.channel); obj.store.manager.deprecateProxy(obj.id, secret.channel);
}); });
} catch (e) {} } catch (e) {}
@ -187,6 +187,7 @@ define([
}); });
}; };
SF.upgrade = function (channel, secret) { SF.upgrade = function (channel, secret) {
var sf = allSharedFolders[channel]; var sf = allSharedFolders[channel];
if (!sf || !sf.readOnly) { return; } if (!sf || !sf.readOnly) { return; }
@ -235,17 +236,21 @@ define([
} }
var nt = nThen; var nt = nThen;
sf.teams.forEach(function (obj) { sf.teams.forEach(function (obj) {
// XXX if we're a viewer in this team, we can't update the keys
nt = nt(function (waitFor) { nt = nt(function (waitFor) {
var s = obj.store; var s = obj.store;
var sfId = obj.id; 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]) || {}; var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
if (!sfId || !shared[sfId]) { return; } if (!sfId || !shared[sfId]) { return; }
var sf = JSON.parse(JSON.stringify(shared[sfId])); var sf = JSON.parse(JSON.stringify(shared[sfId]));
sf.password = password; sf.password = password;
sf.channel = secret.channel;
sf.href = '/drive/#'+Hash.getEditHashFromKeys(secret); // XXX encrypt
sf.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
SF.load({ SF.load({
network: network, network: network,
store: s, store: s,
@ -256,7 +261,9 @@ define([
s.rpc.pin([secret.channel], waitFor()); s.rpc.pin([secret.channel], waitFor());
}).nThen; }).nThen;
}); });
nt(cb); nt(function () {
cb();
});
}; };
/* loadSharedFolders /* loadSharedFolders

@ -71,19 +71,21 @@ define([
cb(null, clone(data[attr])); cb(null, clone(data[attr]));
}; };
exp.pushData = function (data, cb) { exp.pushData = function (_data, cb) {
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
if (readOnly) { return void cb('EFORBIDDEN'); } if (readOnly) { return void cb('EFORBIDDEN'); }
var id = Util.createRandomInteger(); var id = Util.createRandomInteger();
var data = clone(_data);
// If we were given an edit link, encrypt its value if needed // If we were given an edit link, encrypt its value if needed
if (data.href) { data.href = exp.cryptor.encrypt(data.href); } if (data.href) { data.href = exp.cryptor.encrypt(data.href); }
files[FILES_DATA][id] = data; files[FILES_DATA][id] = data;
cb(null, id); cb(null, id);
}; };
exp.pushSharedFolder = function (data, cb) { exp.pushSharedFolder = function (_data, cb) {
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
if (readOnly) { return void cb('EFORBIDDEN'); } if (readOnly) { return void cb('EFORBIDDEN'); }
var data = clone(_data);
// Check if we already have this shared folder in our drive // Check if we already have this shared folder in our drive
var exists; var exists;
@ -476,6 +478,9 @@ define([
files.migrateRo = 1; files.migrateRo = 1;
var next = function () { var next = function () {
var copy = JSON.parse(JSON.stringify(files)); 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) { Object.keys(copy[FILES_DATA]).forEach(function (id) {
var data = copy[FILES_DATA][id] || {}; var data = copy[FILES_DATA][id] || {};
// If this pad has a visible href, encrypt it // 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) { if (data.href && data.roHref && !data.fileType && data.href.indexOf('#') !== -1) {
data.href = exp.cryptor.encrypt(data.href); data.href = exp.cryptor.encrypt(data.href);
} }
}); });*/
copy.version = 2; copy.version = 2;
delete copy.migrateRo; delete copy.migrateRo;

@ -52,6 +52,13 @@ define([
// Password may have changed // Password may have changed
var deprecateProxy = function (Env, id, channel) { 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.unpinPads([channel], function () {});
Env.user.userObject.deprecateSharedFolder(id); Env.user.userObject.deprecateSharedFolder(id);
if (Env.Store && Env.Store.refreshDriveUI) { if (Env.Store && Env.Store.refreshDriveUI) {
@ -554,7 +561,8 @@ define([
var secret = Hash.getSecrets(parsed.type, parsed.hash, newPassword); var secret = Hash.getSecrets(parsed.type, parsed.hash, newPassword);
data.password = newPassword; data.password = newPassword;
data.channel = secret.channel; 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); data.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
_addSharedFolder(Env, { _addSharedFolder(Env, {
path: ['root'], path: ['root'],

@ -15,6 +15,8 @@ define([
var TEMPLATE = module.TEMPLATE = "template"; var TEMPLATE = module.TEMPLATE = "template";
var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders"; var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders";
var SHARED_FOLDERS_TEMP = module.SHARED_FOLDERS_TEMP = "sharedFoldersTemp"; // Maybe deleted or new password 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 // Create untitled documents when no name is given
var getLocaleDate = function () { var getLocaleDate = function () {
@ -66,6 +68,37 @@ define([
return pad.roHref; 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) { module.init = function (files, config) {
var exp = {}; var exp = {};
@ -74,18 +107,19 @@ define([
exp.setReadOnly = function (state, key) { exp.setReadOnly = function (state, key) {
config.editKey = key; config.editKey = key;
createCryptor(key); createCryptor(key);
exp.readOnly = state;
if (exp._setReadOnly) { if (exp._setReadOnly) {
// Change outer // Change outer
exp._setReadOnly(state); exp._setReadOnly(state);
} }
}; };
exp.readOnly = config.readOnly;
exp.reencrypt = module.reencrypt;
exp.getDefaultName = module.getDefaultName; exp.getDefaultName = module.getDefaultName;
var sframeChan = config.sframeChan; 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_FOLDER_NAME = Messages.fm_newFolder || 'New folder';
var NEW_FILE_NAME = Messages.fm_newFile || 'New file'; var NEW_FILE_NAME = Messages.fm_newFile || 'New file';
@ -95,6 +129,8 @@ define([
exp.TEMPLATE = TEMPLATE; exp.TEMPLATE = TEMPLATE;
exp.SHARED_FOLDERS = SHARED_FOLDERS; exp.SHARED_FOLDERS = SHARED_FOLDERS;
exp.SHARED_FOLDERS_TEMP = SHARED_FOLDERS_TEMP; 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; var sharedFolder = exp.sharedFolder = config.sharedFolder;
exp.id = config.id; exp.id = config.id;

Loading…
Cancel
Save