Fix read-only shared folders with password change
parent
0583ed3c8b
commit
f9723a6183
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue