Merge branch 'sfPassword' into ro
This commit is contained in:
@@ -453,7 +453,7 @@ define([
|
||||
|
||||
Store.isNewChannel = function (clientId, data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
|
||||
var channelId = data.channel || Hash.hrefToHexChannelId(data.href, data.password);
|
||||
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
|
||||
if (e) { return void cb({error: e}); }
|
||||
if (response && response.length && typeof(response[0]) === 'boolean') {
|
||||
@@ -1773,21 +1773,24 @@ define([
|
||||
}
|
||||
};
|
||||
};
|
||||
Store.loadSharedFolder = function (teamId, id, data, cb) {
|
||||
Store.loadSharedFolder = function (teamId, id, data, cb, isNew) {
|
||||
var s = getStore(teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
var rt = SF.load({
|
||||
SF.load({
|
||||
isNew: isNew,
|
||||
network: store.network,
|
||||
store: s
|
||||
store: s,
|
||||
isNewChannel: Store.isNewChannel
|
||||
}, id, data, cb);
|
||||
return rt;
|
||||
};
|
||||
var loadSharedFolder = function (id, data, cb) {
|
||||
Store.loadSharedFolder(null, id, data, cb);
|
||||
var loadSharedFolder = function (id, data, cb, isNew) {
|
||||
Store.loadSharedFolder(null, id, data, cb, isNew);
|
||||
};
|
||||
Store.loadSharedFolderAnon = function (clientId, data, cb) {
|
||||
Store.loadSharedFolder(null, data.id, data.data, function () {
|
||||
cb();
|
||||
Store.loadSharedFolder(null, data.id, data.data, function (rt) {
|
||||
cb({
|
||||
error: rt ? undefined : 'EDELETED'
|
||||
});
|
||||
});
|
||||
};
|
||||
Store.addSharedFolder = function (clientId, data, cb) {
|
||||
@@ -1800,6 +1803,9 @@ define([
|
||||
cb(id);
|
||||
});
|
||||
};
|
||||
Store.updateSharedFolderPassword = function (clientId, data, cb) {
|
||||
SF.updatePassword(Store, data, store.network, cb);
|
||||
};
|
||||
|
||||
// Drive
|
||||
Store.userObjectCommand = function (clientId, cmdData, cb) {
|
||||
@@ -1884,6 +1890,27 @@ define([
|
||||
});
|
||||
};
|
||||
registerProxyEvents = function (proxy, fId) {
|
||||
if (!fId) {
|
||||
// Listen for shared folder password change
|
||||
proxy.on('change', ['drive', UserObject.SHARED_FOLDERS], function (o, n, p) {
|
||||
if (p.length > 3 && p[3] === 'password') {
|
||||
var id = p[2];
|
||||
var data = proxy.drive[UserObject.SHARED_FOLDERS][id];
|
||||
var href = store.manager.user.userObject.getHref ?
|
||||
store.manager.user.userObject.getHref(data) : data.href;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
|
||||
SF.updatePassword({
|
||||
oldChannel: secret.channel,
|
||||
password: n,
|
||||
href: href
|
||||
}, store.network, function () {
|
||||
console.log('Shared folder password changed');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
proxy.on('change', [], function (o, n, p) {
|
||||
if (fId) {
|
||||
// Pin the new pads
|
||||
@@ -2035,6 +2062,7 @@ define([
|
||||
unpin: unpin,
|
||||
loadSharedFolder: loadSharedFolder,
|
||||
settings: proxy.settings,
|
||||
Store: Store
|
||||
}, {
|
||||
outer: true,
|
||||
removeOwnedChannel: function (channel, cb) { Store.removeOwnedChannel('', channel, cb); },
|
||||
@@ -2117,6 +2145,7 @@ define([
|
||||
proxy.settings.general.allowUserFeedback = true;
|
||||
}
|
||||
returned.feedback = proxy.settings.general.allowUserFeedback;
|
||||
Feedback.init(returned.feedback);
|
||||
|
||||
if (typeof(cb) === 'function') { cb(returned); }
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@ define([
|
||||
var cb = Util.once(_cb);
|
||||
var network = config.network;
|
||||
var store = config.store;
|
||||
var teamId = store.id || -1;
|
||||
var isNew = config.isNew;
|
||||
var isNewChannel = config.isNewChannel;
|
||||
var teamId = store.id;
|
||||
var handler = store.handleSharedFolder;
|
||||
|
||||
var href = store.manager.user.userObject.getHref(data);
|
||||
@@ -76,85 +78,109 @@ define([
|
||||
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
|
||||
var secondaryKey = secret.keys.secondaryKey;
|
||||
|
||||
var sf = allSharedFolders[secret.channel];
|
||||
if (sf && sf.readOnly && secondaryKey) {
|
||||
// We were in readOnly mode and now we know the edit keys!
|
||||
SF.upgrade(secret.channel, secret);
|
||||
}
|
||||
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); };
|
||||
var uo = store.manager.addProxy(id, sf.rt, leave, secondaryKey);
|
||||
SF.checkMigration(secondaryKey, sf.rt.proxy, uo, function () {
|
||||
cb(sf.rt, sf.metadata);
|
||||
// If we try to load an existing shared folder (isNew === false) but this folder
|
||||
// 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];
|
||||
if (sf && sf.readOnly && secondaryKey) {
|
||||
// We were in readOnly mode and now we know the edit keys!
|
||||
SF.upgrade(secret.channel, secret);
|
||||
}
|
||||
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); };
|
||||
var uo = store.manager.addProxy(id, sf.rt, leave, secondaryKey);
|
||||
SF.checkMigration(secondaryKey, sf.rt.proxy, uo, function () {
|
||||
cb(sf.rt, sf.metadata);
|
||||
});
|
||||
});
|
||||
});
|
||||
sf.team.push(teamId);
|
||||
if (handler) { handler(id, sf.rt); }
|
||||
return sf.rt;
|
||||
}
|
||||
if (sf && sf.queue && sf.rt) {
|
||||
// The shared folder is loading, add our callbacks to the queue
|
||||
sf.queue.push({
|
||||
cb: cb,
|
||||
store: store,
|
||||
id: id
|
||||
});
|
||||
sf.team.push(teamId);
|
||||
if (handler) { handler(id, sf.rt); }
|
||||
return sf.rt;
|
||||
}
|
||||
|
||||
sf = allSharedFolders[secret.channel] = {
|
||||
queue: [{
|
||||
cb: cb,
|
||||
store: store,
|
||||
id: id
|
||||
}],
|
||||
team: [store.id || -1],
|
||||
readOnly: Boolean(secondaryKey)
|
||||
};
|
||||
|
||||
var owners = data.owners;
|
||||
var listmapConfig = {
|
||||
data: {},
|
||||
channel: secret.channel,
|
||||
readOnly: Boolean(secondaryKey),
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
userName: 'sharedFolder',
|
||||
logLevel: 1,
|
||||
ChainPad: ChainPad,
|
||||
classic: true,
|
||||
network: network,
|
||||
metadata: {
|
||||
validateKey: secret.keys.validateKey || undefined,
|
||||
owners: owners
|
||||
sf.teams.push(store);
|
||||
if (handler) { handler(id, sf.rt); }
|
||||
return sf.rt;
|
||||
}
|
||||
};
|
||||
var rt = sf.rt = Listmap.create(listmapConfig);
|
||||
rt.proxy.on('ready', function (info) {
|
||||
if (!Object.keys(rt.proxy).length) {
|
||||
// New Shared folder: no migration required
|
||||
rt.proxy.version = 2;
|
||||
}
|
||||
if (!sf.queue) {
|
||||
return;
|
||||
}
|
||||
sf.leave = info.leave;
|
||||
sf.metadata = info.metadata;
|
||||
sf.queue.forEach(function (obj) {
|
||||
var leave = function () { SF.leave(secret.channel, teamId); };
|
||||
var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey);
|
||||
SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
|
||||
obj.cb(sf.rt, sf.metadata);
|
||||
if (sf && sf.queue && sf.rt) {
|
||||
// The shared folder is loading, add our callbacks to the queue
|
||||
sf.queue.push({
|
||||
cb: cb,
|
||||
store: store,
|
||||
id: id
|
||||
});
|
||||
sf.teams.push(store);
|
||||
if (handler) { handler(id, sf.rt); }
|
||||
return sf.rt;
|
||||
}
|
||||
|
||||
sf = allSharedFolders[secret.channel] = {
|
||||
queue: [{
|
||||
cb: cb,
|
||||
store: store,
|
||||
id: id
|
||||
}],
|
||||
teams: [store],
|
||||
readOnly: Boolean(secondaryKey)
|
||||
};
|
||||
|
||||
var owners = data.owners;
|
||||
var listmapConfig = {
|
||||
data: {},
|
||||
channel: secret.channel,
|
||||
readOnly: Boolean(secondaryKey),
|
||||
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) {
|
||||
if (isNew && !Object.keys(rt.proxy).length) {
|
||||
// New Shared folder: no migration required
|
||||
rt.proxy.version = 2;
|
||||
}
|
||||
if (!sf.queue) {
|
||||
return;
|
||||
}
|
||||
sf.queue.forEach(function (obj) {
|
||||
var leave = function () { SF.leave(secret.channel, teamId); };
|
||||
var uo = obj.store.manager.addProxy(obj.id, rt, leave, secondaryKey);
|
||||
SF.checkMigration(secondaryKey, rt.proxy, uo, function () {
|
||||
obj.cb(sf.rt, info.metadata);
|
||||
});
|
||||
});
|
||||
sf.metadata = info.metadata;
|
||||
sf.ready = true;
|
||||
delete sf.queue;
|
||||
});
|
||||
sf.ready = true;
|
||||
delete sf.queue;
|
||||
rt.proxy.on('error', function (info) {
|
||||
if (info && info.error) {
|
||||
if (info.error === "EDELETED" ) {
|
||||
try {
|
||||
// Deprecate the shared folder from each team
|
||||
sf.teams.forEach(function (store) {
|
||||
store.manager.deprecateProxy(id, secret.channel);
|
||||
});
|
||||
} catch (e) {}
|
||||
delete allSharedFolders[secret.channel];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (handler) { handler(id, rt); }
|
||||
});
|
||||
if (handler) { handler(id, rt); }
|
||||
return rt;
|
||||
};
|
||||
|
||||
SF.upgrade = function (channel, secret) {
|
||||
@@ -173,8 +199,14 @@ define([
|
||||
if (!sf) { return; }
|
||||
var clients = sf.teams;
|
||||
if (!Array.isArray(clients)) { return; }
|
||||
var idx = clients.indexOf(teamId);
|
||||
if (idx === -1) { return; }
|
||||
var idx;
|
||||
clients.some(function (store, i) {
|
||||
if (store.id === teamId) {
|
||||
idx = i;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (typeof (idx) === "undefined") { return; }
|
||||
// Remove the selected team
|
||||
clients.splice(idx, 1);
|
||||
|
||||
@@ -185,6 +217,38 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
sf.rt.stop();
|
||||
}
|
||||
var nt = nThen;
|
||||
sf.teams.forEach(function (s) {
|
||||
nt = nt(function (waitFor) {
|
||||
var sfId = s.manager.user.userObject.getSFIdFromHref(href);
|
||||
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(cb);
|
||||
};
|
||||
|
||||
/* loadSharedFolders
|
||||
load all shared folder stored in a given drive
|
||||
- store: user or team main store
|
||||
@@ -193,35 +257,13 @@ define([
|
||||
*/
|
||||
SF.loadSharedFolders = function (Store, network, store, userObject, waitFor) {
|
||||
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
|
||||
// Check if any of our shared folder is expired or deleted by its owner.
|
||||
// If we don't check now, Listmap will create an empty proxy if it no longer exists on
|
||||
// the server.
|
||||
nThen(function (waitFor) {
|
||||
var checkExpired = Object.keys(shared).map(function (fId) {
|
||||
return shared[fId].channel;
|
||||
});
|
||||
Store.getDeletedPads(null, {list: checkExpired}, waitFor(function (chans) {
|
||||
if (chans && chans.error) { return void console.error(chans.error); }
|
||||
if (!Array.isArray(chans) || !chans.length) { return; }
|
||||
var toDelete = [];
|
||||
Object.keys(shared).forEach(function (fId) {
|
||||
if (chans.indexOf(shared[fId].channel) !== -1
|
||||
&& toDelete.indexOf(fId) === -1) {
|
||||
toDelete.push(fId);
|
||||
}
|
||||
});
|
||||
toDelete.forEach(function (fId) {
|
||||
var paths = userObject.findFile(Number(fId));
|
||||
userObject.delete(paths, waitFor(), true);
|
||||
delete shared[fId];
|
||||
});
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
Object.keys(shared).forEach(function (id) {
|
||||
var sf = shared[id];
|
||||
SF.load({
|
||||
network: network,
|
||||
store: store
|
||||
store: store,
|
||||
isNewChannel: Store.isNewChannel
|
||||
}, id, sf, waitFor());
|
||||
});
|
||||
}).nThen(waitFor());
|
||||
|
||||
@@ -56,6 +56,7 @@ define([
|
||||
ADD_SHARED_FOLDER: Store.addSharedFolder,
|
||||
LOAD_SHARED_FOLDER: Store.loadSharedFolderAnon,
|
||||
RESTORE_SHARED_FOLDER: Store.restoreSharedFolder,
|
||||
UPDATE_SHARED_FOLDER_PASSWORD: Store.updateSharedFolderPassword,
|
||||
// Messaging
|
||||
ANSWER_FRIEND_REQUEST: Store.answerFriendRequest,
|
||||
SEND_FRIEND_REQUEST: Store.sendFriendRequest,
|
||||
|
||||
@@ -9,6 +9,7 @@ define([
|
||||
'/common/outer/sharedfolder.js',
|
||||
'/common/outer/roster.js',
|
||||
'/common/common-messaging.js',
|
||||
'/common/common-feedback.js',
|
||||
|
||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||
'/bower_components/chainpad-crypto/crypto.js',
|
||||
@@ -18,7 +19,7 @@ define([
|
||||
'/bower_components/saferphore/index.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (Util, Hash, Constants, Realtime,
|
||||
ProxyManager, UserObject, SF, Roster, Messaging,
|
||||
ProxyManager, UserObject, SF, Roster, Messaging, Feedback,
|
||||
Listmap, Crypto, CpNetflux, ChainPad, nThen, Saferphore) {
|
||||
var Team = {};
|
||||
|
||||
@@ -30,6 +31,27 @@ define([
|
||||
|
||||
var registerChangeEvents = function (ctx, team, proxy, fId) {
|
||||
if (!team) { return; }
|
||||
if (!fId) {
|
||||
// Listen for shared folder password change
|
||||
proxy.on('change', ['drive', UserObject.SHARED_FOLDERS], function (o, n, p) {
|
||||
if (p.length > 3 && p[3] === 'password') {
|
||||
var id = p[2];
|
||||
var data = proxy.drive[UserObject.SHARED_FOLDERS][id];
|
||||
var href = team.manager.user.userObject.getHref ?
|
||||
team.manager.user.userObject.getHref(data) : data.href;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
|
||||
SF.updatePassword(ctx.Store, {
|
||||
oldChannel: secret.channel,
|
||||
password: n,
|
||||
href: href
|
||||
}, ctx.store.network, function () {
|
||||
console.log('Shared folder password changed');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
proxy.on('change', [], function (o, n, p) {
|
||||
if (fId) {
|
||||
// Pin the new pads
|
||||
@@ -216,13 +238,13 @@ define([
|
||||
}));
|
||||
}).nThen(function () {
|
||||
// Create the proxy manager
|
||||
var loadSharedFolder = function (id, data, cb) {
|
||||
var loadSharedFolder = function (id, data, cb, isNew) {
|
||||
SF.load({
|
||||
isNew: isNew,
|
||||
network: ctx.store.network,
|
||||
store: team
|
||||
}, id, data, function (id, rt) {
|
||||
cb(id, rt);
|
||||
});
|
||||
store: team,
|
||||
isNewChannel: ctx.Store.isNewChannel
|
||||
}, id, data, cb);
|
||||
};
|
||||
var teamData = ctx.store.proxy.teams[team.id];
|
||||
var hash = teamData.hash || teamData.roHash;
|
||||
@@ -236,6 +258,7 @@ define([
|
||||
settings: {
|
||||
drive: Util.find(ctx.store, ['proxy', 'settings', 'drive'])
|
||||
},
|
||||
Store: ctx.Store
|
||||
}, {
|
||||
outer: true,
|
||||
removeOwnedChannel: function (channel, cb) {
|
||||
@@ -567,6 +590,7 @@ define([
|
||||
proxy.drive = {};
|
||||
|
||||
onReady(ctx, id, lm, roster, keys, cId, function () {
|
||||
Feedback.send('TEAM_CREATION');
|
||||
ctx.updateMetadata();
|
||||
cb();
|
||||
});
|
||||
@@ -682,6 +706,7 @@ define([
|
||||
if (err) { console.error(err); }
|
||||
}));
|
||||
}).nThen(function () {
|
||||
Feedback.send('TEAM_DELETION');
|
||||
closeTeam(ctx, teamId);
|
||||
cb();
|
||||
});
|
||||
|
||||
@@ -30,6 +30,7 @@ define([
|
||||
var TRASH = exp.TRASH;
|
||||
var TEMPLATE = exp.TEMPLATE;
|
||||
var SHARED_FOLDERS = exp.SHARED_FOLDERS;
|
||||
var SHARED_FOLDERS_TEMP = exp.SHARED_FOLDERS_TEMP;
|
||||
|
||||
var debug = exp.debug;
|
||||
|
||||
@@ -109,6 +110,15 @@ define([
|
||||
cb(null, id);
|
||||
};
|
||||
|
||||
exp.deprecateSharedFolder = function (id) {
|
||||
var data = files[SHARED_FOLDERS][id];
|
||||
if (!data) { return; }
|
||||
files[SHARED_FOLDERS_TEMP][id] = JSON.parse(JSON.stringify(data));
|
||||
var paths = exp.findFile(Number(id));
|
||||
exp.delete(paths, null, true);
|
||||
delete files[SHARED_FOLDERS][id];
|
||||
};
|
||||
|
||||
// FILES DATA
|
||||
var spliceFileData = function (id) {
|
||||
if (readOnly) { return; }
|
||||
@@ -868,6 +878,22 @@ define([
|
||||
}
|
||||
}
|
||||
};
|
||||
var fixSharedFoldersTemp = function () {
|
||||
if (sharedFolder) { return; }
|
||||
if (typeof(files[SHARED_FOLDERS_TEMP]) !== "object") {
|
||||
debug("SHARED_FOLDER_TEMP was not an object");
|
||||
files[SHARED_FOLDERS_TEMP] = {};
|
||||
}
|
||||
// Remove deprecated shared folder if they were already added back
|
||||
var sft = files[SHARED_FOLDERS_TEMP];
|
||||
var sf = files[SHARED_FOLDERS];
|
||||
for (var id in sft) {
|
||||
if (sf[id]) {
|
||||
delete sft[id];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var fixDrive = function () {
|
||||
Object.keys(files).forEach(function (key) {
|
||||
@@ -881,6 +907,7 @@ define([
|
||||
fixFilesData();
|
||||
fixDrive();
|
||||
fixSharedFolders();
|
||||
fixSharedFoldersTemp();
|
||||
|
||||
var ms = (+new Date() - t0) + 'ms';
|
||||
if (JSON.stringify(files) !== before) {
|
||||
|
||||
Reference in New Issue
Block a user