cryptpad/www/common/outer/sharedfolder.js

289 lines
10 KiB
JavaScript
Raw Normal View History

2019-09-04 15:14:44 +00:00
define([
'/common/common-hash.js',
'/common/common-util.js',
'/common/userObject.js',
2019-09-04 15:14:44 +00:00
'/bower_components/nthen/index.js',
2019-09-04 15:14:44 +00:00
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/chainpad/chainpad.dist.js',
], function (Hash, Util, UserObject,
nThen, Crypto, Listmap, ChainPad) {
2019-09-04 15:14:44 +00:00
var SF = {};
/* load
create and load a proxy using listmap for a given shared folder
- config: network and "manager" (either the user one or a team manager)
- id: shared folder id
*/
var allSharedFolders = {};
2019-10-10 10:35:01 +00:00
// No version: visible edit
// Version 2: encrypted edit links
SF.checkMigration = function (secondaryKey, proxy, uo, cb) {
if (true) { // XXX remove this block to enable migration at load time
// FIXME history
return void cb();
}
var drive = proxy.drive || proxy;
// View access: can't migrate
if (!secondaryKey) { return void cb(); }
// Already migrated: nothing to do
if (drive.version >= 2) { return void cb(); }
// Not yet migrating: migrate
if (!drive.migrateRo) { return void uo.migrateReadOnly(cb); }
// Already migrating: wait for the end...
var done = false;
var to;
var it = setInterval(function () {
if (drive.version >= 2) {
done = true;
clearTimeout(to);
clearInterval(it);
return void cb();
}
}, 100);
2019-10-10 11:56:55 +00:00
to = setTimeout(function () {
2019-10-10 10:35:01 +00:00
clearInterval(it);
uo.migrateReadOnly(function () {
done = true;
cb();
});
}, 20000);
var path = proxy.drive ? ['drive', 'version'] : ['version'];
proxy.on('change', path, function () {
if (done) { return; }
if (drive.version >= 2) {
done = true;
clearTimeout(to);
clearInterval(it);
cb();
}
});
};
SF.load = function (config, id, data, _cb) {
var cb = Util.once(_cb);
2019-09-04 15:14:44 +00:00
var network = config.network;
var store = config.store;
2019-10-21 13:22:59 +00:00
var isNew = config.isNew;
var isNewChannel = config.isNewChannel;
var teamId = store.id;
var handler = store.handleSharedFolder;
2019-09-04 15:14:44 +00:00
2019-10-07 16:30:46 +00:00
var href = store.manager.user.userObject.getHref(data);
var parsed = Hash.parsePadUrl(href);
2019-09-04 15:14:44 +00:00
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
// If we don' have valid keys, abort and remove the proxy to make sure
// we don't block the drive permanently
if (!secret.keys) {
store.manager.deprecateProxy(id);
return void cb(null);
}
2019-10-07 12:35:11 +00:00
var secondaryKey = secret.keys.secondaryKey;
2019-10-21 15:43:46 +00:00
// If we try to load an existing shared folder (isNew === false) but this folder
2019-10-21 13:22:59 +00:00
// doesn't exist in the database, abort and cb
nThen(function (waitFor) {
isNewChannel(null, { channel: secret.channel }, waitFor(function (obj) {
if (obj.isNew && !isNew) {
store.manager.deprecateProxy(id, secret.channel);
waitFor.abort();
return void cb(null);
}
}));
}).nThen(function () {
var sf = allSharedFolders[secret.channel];
2019-10-21 15:43:46 +00:00
if (sf && sf.readOnly && secondaryKey) {
// We were in readOnly mode and now we know the edit keys!
SF.upgrade(secret.channel, secret);
2019-09-04 15:14:44 +00:00
}
2019-10-21 13:22:59 +00:00
if (sf && sf.ready && sf.rt) {
// The shared folder is already loaded, return its data
setTimeout(function () {
var leave = function () { SF.leave(secret.channel, teamId); };
2019-10-21 15:43:46 +00:00
var uo = store.manager.addProxy(id, sf.rt, leave, secondaryKey);
SF.checkMigration(secondaryKey, sf.rt.proxy, uo, function () {
cb(sf.rt, sf.metadata);
});
2019-10-21 13:22:59 +00:00
});
sf.teams.push({
cb: cb,
store: store,
id: id
});
2019-10-21 13:22:59 +00:00
if (handler) { handler(id, sf.rt); }
return;
2019-10-14 15:37:54 +00:00
}
if (sf && !sf.ready && sf.rt) {
2019-10-21 13:22:59 +00:00
// The shared folder is loading, add our callbacks to the queue
sf.teams.push({
2019-10-21 13:22:59 +00:00
cb: cb,
store: store,
secondaryKey: secondaryKey,
2019-10-21 13:22:59 +00:00
id: id
});
if (handler) { handler(id, sf.rt); }
return;
}
2019-10-21 13:22:59 +00:00
sf = allSharedFolders[secret.channel] = {
teams: [{
2019-10-21 13:22:59 +00:00
cb: cb,
store: store,
secondaryKey: secondaryKey,
2019-10-21 13:22:59 +00:00
id: id
}],
readOnly: !Boolean(secondaryKey)
2019-10-21 13:22:59 +00:00
};
var owners = data.owners;
var listmapConfig = {
data: {},
channel: secret.channel,
readOnly: !Boolean(secondaryKey),
2019-10-21 13:22:59 +00:00
crypto: Crypto.createEncryptor(secret.keys),
userName: 'sharedFolder',
logLevel: 1,
ChainPad: ChainPad,
classic: true,
network: network,
metadata: {
validateKey: secret.keys.validateKey || undefined,
owners: owners
}
};
var rt = sf.rt = Listmap.create(listmapConfig);
rt.proxy.on('ready', function (info) {
2019-10-21 15:43:46 +00:00
if (isNew && !Object.keys(rt.proxy).length) {
// New Shared folder: no migration required
rt.proxy.version = 2;
}
if (!sf.teams) {
2019-10-21 13:22:59 +00:00
return;
}
sf.teams.forEach(function (obj) {
2019-10-21 13:22:59 +00:00
var leave = function () { SF.leave(secret.channel, teamId); };
var uo = obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey);
2019-10-21 15:43:46 +00:00
SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
obj.cb(sf.rt, info.metadata);
});
2019-10-10 10:35:01 +00:00
});
2019-10-21 13:22:59 +00:00
sf.metadata = info.metadata;
sf.ready = true;
});
rt.proxy.on('error', function (info) {
if (info && info.error) {
if (info.error === "EDELETED" ) {
try {
// Deprecate the shared folder from each team
// We can only hide it
sf.teams.forEach(function (obj) {
obj.store.manager.deprecateProxy(obj.id, secret.channel);
2019-10-21 13:22:59 +00:00
});
} catch (e) {}
delete allSharedFolders[secret.channel];
}
}
});
2019-10-21 13:22:59 +00:00
if (handler) { handler(id, rt); }
2019-09-04 15:14:44 +00:00
});
};
SF.upgrade = function (channel, secret) {
var sf = allSharedFolders[channel];
if (!sf || !sf.readOnly) { return; }
if (!sf.rt.setReadOnly) { return; }
if (!secret.keys || !secret.keys.editKeyStr) { return; }
var crypto = Crypto.createEncryptor(secret.keys);
sf.readOnly = false;
sf.rt.setReadOnly(false, crypto);
};
SF.leave = function (channel, teamId) {
var sf = allSharedFolders[channel];
if (!sf) { return; }
var clients = sf.teams;
if (!Array.isArray(clients)) { return; }
2019-10-21 13:22:59 +00:00
var idx;
clients.some(function (obj, i) {
if (obj.store.id === teamId) {
2019-10-21 13:22:59 +00:00
idx = i;
return true;
}
});
if (typeof (idx) === "undefined") { return; }
// Remove the selected team
clients.splice(idx, 1);
//If all the teams have closed this shared folder, stop it
if (clients.length) { return; }
if (sf.rt && sf.rt.stop) {
sf.rt.stop();
}
};
// Update the password locally
2019-10-21 13:22:59 +00:00
SF.updatePassword = function (Store, data, network, cb) {
var oldChannel = data.oldChannel;
var href = data.href;
var password = data.password;
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
var sf = allSharedFolders[oldChannel];
if (!sf) { return void cb({ error: 'ENOTFOUND' }); }
if (sf.rt && sf.rt.stop) {
try { sf.rt.stop(); } catch (e) {}
2019-10-21 13:22:59 +00:00
}
var nt = nThen;
sf.teams.forEach(function (obj) {
2019-10-21 13:22:59 +00:00
nt = nt(function (waitFor) {
var s = obj.store;
var sfId = obj.id;
2019-10-21 13:22:59 +00:00
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.load({
network: network,
store: s,
isNewChannel: Store.isNewChannel
}, sfId, sf, waitFor());
if (!s.rpc) { return; }
s.rpc.unpin([oldChannel], waitFor());
s.rpc.pin([secret.channel], waitFor());
}).nThen;
});
nt(function () {
cb();
});
2019-10-21 13:22:59 +00:00
};
/* loadSharedFolders
load all shared folder stored in a given drive
- store: user or team main store
- userObject: userObject associated to the main drive
- handler: a function (sfid, rt) called for each shared folder loaded
*/
SF.loadSharedFolders = function (Store, network, store, userObject, waitFor) {
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
nThen(function (waitFor) {
Object.keys(shared).forEach(function (id) {
var sf = shared[id];
2019-09-06 13:45:56 +00:00
SF.load({
network: network,
2019-10-21 13:22:59 +00:00
store: store,
isNewChannel: Store.isNewChannel
}, id, sf, waitFor());
});
}).nThen(waitFor());
};
2019-09-04 15:14:44 +00:00
return SF;
});