Upgrade/downgrade shared folders access rights
This commit is contained in:
parent
8ca7e11150
commit
d443c93893
@ -158,11 +158,12 @@ 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;
|
||||||
postMessage("ADD_SHARED_FOLDER", {
|
postMessage("ADD_SHARED_FOLDER", {
|
||||||
teamId: teamId,
|
teamId: teamId,
|
||||||
path: ['root'],
|
path: ['root'],
|
||||||
folderData: {
|
folderData: {
|
||||||
href: '/drive/#' + Hash.getEditHashFromKeys(secret),
|
href: href,
|
||||||
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
|
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
password: secret.password,
|
password: secret.password,
|
||||||
|
|||||||
@ -544,7 +544,7 @@ define([
|
|||||||
Object.keys(folders).forEach(function (id) {
|
Object.keys(folders).forEach(function (id) {
|
||||||
var f = folders[id];
|
var f = folders[id];
|
||||||
var sfData = files.sharedFolders[id] || {};
|
var sfData = files.sharedFolders[id] || {};
|
||||||
var parsed = Hash.parsePadUrl(sfData.href);
|
var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref);
|
||||||
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
||||||
manager.addProxy(id, {proxy: f}, null, secret.keys.secondaryKey);
|
manager.addProxy(id, {proxy: f}, null, secret.keys.secondaryKey);
|
||||||
});
|
});
|
||||||
@ -2509,6 +2509,7 @@ define([
|
|||||||
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
|
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
|
||||||
var data = manager.getSharedFolderData(id);
|
var data = manager.getSharedFolderData(id);
|
||||||
var parsed = Hash.parsePadUrl(data.href);
|
var parsed = Hash.parsePadUrl(data.href);
|
||||||
|
// XXX share modal shared folder read only
|
||||||
if (!parsed || !parsed.hash) { return void console.error("Invalid href: "+data.href); }
|
if (!parsed || !parsed.hash) { return void console.error("Invalid href: "+data.href); }
|
||||||
var friends = common.getFriends();
|
var friends = common.getFriends();
|
||||||
var teams = common.getMetadataMgr().getPrivateData().teams;
|
var teams = common.getMetadataMgr().getPrivateData().teams;
|
||||||
|
|||||||
@ -27,6 +27,7 @@ define([
|
|||||||
if (parsed) {
|
if (parsed) {
|
||||||
var proxy = proxyData.proxy;
|
var proxy = proxyData.proxy;
|
||||||
var oldFo = FO.init(parsed.drive, {
|
var oldFo = FO.init(parsed.drive, {
|
||||||
|
readOnly: false,
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
outer: true
|
outer: true
|
||||||
});
|
});
|
||||||
|
|||||||
@ -43,7 +43,7 @@ define([
|
|||||||
MessengerUI.create = function ($container, common, toolbar) {
|
MessengerUI.create = function ($container, common, toolbar) {
|
||||||
var metadataMgr = common.getMetadataMgr();
|
var metadataMgr = common.getMetadataMgr();
|
||||||
var origin = metadataMgr.getPrivateData().origin;
|
var origin = metadataMgr.getPrivateData().origin;
|
||||||
var readOnly = metadataMgr.getPrivateData().readOnly;
|
var readOnly = metadataMgr.getPrivateData().readOnly || toolbar.readOnly;
|
||||||
|
|
||||||
var isApp = typeof(toolbar) !== "undefined";
|
var isApp = typeof(toolbar) !== "undefined";
|
||||||
|
|
||||||
|
|||||||
@ -415,6 +415,41 @@ define([
|
|||||||
cb(false);
|
cb(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handlers['TEAM_EDIT_RIGHTS'] = function (ctx, box, data, cb) {
|
||||||
|
var msg = data.msg;
|
||||||
|
var content = msg.content;
|
||||||
|
|
||||||
|
if (msg.author !== content.user.curvePublic) { return void cb(true); }
|
||||||
|
if (!content.teamData) {
|
||||||
|
console.log('Remove invalid notification');
|
||||||
|
return void cb(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we are a member of this team
|
||||||
|
var myTeams = Util.find(ctx, ['store', 'proxy', 'teams']) || {};
|
||||||
|
var teamId;
|
||||||
|
var team;
|
||||||
|
Object.keys(myTeams).some(function (k) {
|
||||||
|
var _team = myTeams[k];
|
||||||
|
if (_team.channel === content.teamChannel) {
|
||||||
|
teamId = k;
|
||||||
|
team = _team;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!teamId) { return void cb(true); }
|
||||||
|
|
||||||
|
var dismiss = false;
|
||||||
|
try {
|
||||||
|
var module = ctx.store.modules['team'];
|
||||||
|
// changeMyRights returns true if we can't change our rights
|
||||||
|
dismiss = module.changeMyRights(teamId, content.state, content.teamData);
|
||||||
|
} catch (e) { console.error(e); }
|
||||||
|
|
||||||
|
cb(dismiss);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
add: function (ctx, box, data, cb) {
|
add: function (ctx, box, data, cb) {
|
||||||
|
|||||||
@ -814,6 +814,7 @@ define([
|
|||||||
var cb = Util.once(Util.mkAsync(function () {
|
var cb = Util.once(Util.mkAsync(function () {
|
||||||
ctx.emit('TEAMCHAT_READY', chanId, [clientId]);
|
ctx.emit('TEAMCHAT_READY', chanId, [clientId]);
|
||||||
_cb({
|
_cb({
|
||||||
|
readOnly: typeof(secret.keys) === "object" && !secret.keys.validateKey,
|
||||||
channel: chanId
|
channel: chanId
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -168,8 +168,8 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
|
|||||||
if (members[curve]) { throw new Error("ALREADY_PRESENT"); }
|
if (members[curve]) { throw new Error("ALREADY_PRESENT"); }
|
||||||
|
|
||||||
var data = args[curve];
|
var data = args[curve];
|
||||||
// if no role was provided, assume VIEWER
|
// if no role was provided, assume MEMBER
|
||||||
if (typeof(data.role) !== 'string') { data.role = 'VIEWER'; }
|
if (typeof(data.role) !== 'string') { data.role = 'MEMBER'; }
|
||||||
|
|
||||||
if (!canAddRole(author, data.role, members)) {
|
if (!canAddRole(author, data.role, members)) {
|
||||||
throw new Error("INSUFFICIENT_PERMISSIONS");
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
||||||
|
|||||||
@ -77,6 +77,10 @@ define([
|
|||||||
var secondaryKey = secret.keys.secondaryKey;
|
var secondaryKey = secret.keys.secondaryKey;
|
||||||
|
|
||||||
var sf = allSharedFolders[secret.channel];
|
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) {
|
if (sf && sf.ready && sf.rt) {
|
||||||
// The shared folder is already loaded, return its data
|
// The shared folder is already loaded, return its data
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
@ -108,14 +112,15 @@ define([
|
|||||||
store: store,
|
store: store,
|
||||||
id: id
|
id: id
|
||||||
}],
|
}],
|
||||||
team: [store.id || -1]
|
team: [store.id || -1],
|
||||||
|
readOnly: Boolean(secondaryKey)
|
||||||
};
|
};
|
||||||
|
|
||||||
var owners = data.owners;
|
var owners = data.owners;
|
||||||
var listmapConfig = {
|
var listmapConfig = {
|
||||||
data: {},
|
data: {},
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
readOnly: secret.keys && !secret.keys.editKeyStr,
|
readOnly: Boolean(secondaryKey),
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
userName: 'sharedFolder',
|
userName: 'sharedFolder',
|
||||||
logLevel: 1,
|
logLevel: 1,
|
||||||
@ -148,6 +153,17 @@ define([
|
|||||||
return rt;
|
return rt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
SF.leave = function (channel, teamId) {
|
||||||
var sf = allSharedFolders[channel];
|
var sf = allSharedFolders[channel];
|
||||||
if (!sf) { return; }
|
if (!sf) { return; }
|
||||||
|
|||||||
@ -81,6 +81,7 @@ define([
|
|||||||
try { team.listmap.stop(); } catch (e) {}
|
try { team.listmap.stop(); } catch (e) {}
|
||||||
try { team.roster.stop(); } catch (e) {}
|
try { team.roster.stop(); } catch (e) {}
|
||||||
team.proxy = {};
|
team.proxy = {};
|
||||||
|
team.stopped = true;
|
||||||
delete ctx.teams[teamId];
|
delete ctx.teams[teamId];
|
||||||
delete ctx.store.proxy.teams[teamId];
|
delete ctx.store.proxy.teams[teamId];
|
||||||
ctx.emit('LEAVE_TEAM', teamId, team.clients);
|
ctx.emit('LEAVE_TEAM', teamId, team.clients);
|
||||||
@ -140,8 +141,10 @@ define([
|
|||||||
roster: roster
|
roster: roster
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Subscribe to events
|
||||||
if (cId) { team.clients.push(cId); }
|
if (cId) { team.clients.push(cId); }
|
||||||
|
|
||||||
|
// Listen for roster changes
|
||||||
roster.on('change', function () {
|
roster.on('change', function () {
|
||||||
var state = roster.getState();
|
var state = roster.getState();
|
||||||
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
|
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
|
||||||
@ -158,16 +161,19 @@ define([
|
|||||||
rosterData.lastKnownHash = hash;
|
rosterData.lastKnownHash = hash;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update metadata
|
||||||
var state = roster.getState();
|
var state = roster.getState();
|
||||||
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
|
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', id]);
|
||||||
if (teamData) { teamData.metadata = state.metadata; }
|
if (teamData) { teamData.metadata = state.metadata; }
|
||||||
|
|
||||||
|
// Broadcast an event to all the tabs displaying this team
|
||||||
team.sendEvent = function (q, data, sender) {
|
team.sendEvent = function (q, data, sender) {
|
||||||
ctx.emit(q, data, team.clients.filter(function (cId) {
|
ctx.emit(q, data, team.clients.filter(function (cId) {
|
||||||
return cId !== sender;
|
return cId !== sender;
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Provide team chat keys to the messenger app
|
||||||
team.getChatData = function () {
|
team.getChatData = function () {
|
||||||
var chatKeys = keys.chat || {};
|
var chatKeys = keys.chat || {};
|
||||||
var hash = chatKeys.edit || chatKeys.view;
|
var hash = chatKeys.edit || chatKeys.view;
|
||||||
@ -177,7 +183,7 @@ define([
|
|||||||
teamId: id,
|
teamId: id,
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
validateKey: secret.keys.validateKey
|
validateKey: chatKeys.validateKey
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,6 +191,7 @@ define([
|
|||||||
team.pin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
|
team.pin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
|
||||||
team.unpin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
|
team.unpin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); };
|
||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
|
// Init Team RPC
|
||||||
if (!keys.drive.edPrivate) { return; }
|
if (!keys.drive.edPrivate) { return; }
|
||||||
initRpc(ctx, team, keys.drive, waitFor(function (err) {
|
initRpc(ctx, team, keys.drive, waitFor(function (err) {
|
||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
@ -208,6 +215,7 @@ define([
|
|||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
|
// Create the proxy manager
|
||||||
var loadSharedFolder = function (id, data, cb) {
|
var loadSharedFolder = function (id, data, cb) {
|
||||||
SF.load({
|
SF.load({
|
||||||
network: ctx.store.network,
|
network: ctx.store.network,
|
||||||
@ -217,9 +225,8 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
var teamData = ctx.store.proxy.teams[team.id];
|
var teamData = ctx.store.proxy.teams[team.id];
|
||||||
if (teamData) {
|
var hash = teamData.hash || teamData.roHash;
|
||||||
secret = Hash.getSecrets('team', teamData.hash, teamData.password);
|
secret = Hash.getSecrets('team', hash, teamData.password);
|
||||||
}
|
|
||||||
var manager = team.manager = ProxyManager.create(proxy.drive, {
|
var manager = team.manager = ProxyManager.create(proxy.drive, {
|
||||||
onSync: function (cb) { ctx.Store.onSync(id, cb); },
|
onSync: function (cb) { ctx.Store.onSync(id, cb); },
|
||||||
edPublic: keys.drive.edPublic,
|
edPublic: keys.drive.edPublic,
|
||||||
@ -251,12 +258,14 @@ define([
|
|||||||
team.sendEvent("DRIVE_LOG", msg);
|
team.sendEvent("DRIVE_LOG", msg);
|
||||||
},
|
},
|
||||||
rt: team.realtime,
|
rt: team.realtime,
|
||||||
editKey: secret && secret.keys.secondaryKey
|
editKey: secret.keys.secondaryKey,
|
||||||
|
readOnly: Boolean(!secret.keys.secondaryKey)
|
||||||
});
|
});
|
||||||
team.secondaryKey = secret && secret.keys.secondaryKey;
|
team.secondaryKey = secret && secret.keys.secondaryKey;
|
||||||
team.userObject = manager.user.userObject;
|
team.userObject = manager.user.userObject;
|
||||||
team.userObject.fixFiles();
|
team.userObject.fixFiles();
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
|
// Load the shared folders
|
||||||
ctx.teams[id] = team;
|
ctx.teams[id] = team;
|
||||||
registerChangeEvents(ctx, team, proxy);
|
registerChangeEvents(ctx, team, proxy);
|
||||||
SF.checkMigration(team.secondaryKey, proxy, team.userObject, waitFor());
|
SF.checkMigration(team.secondaryKey, proxy, team.userObject, waitFor());
|
||||||
@ -296,10 +305,20 @@ define([
|
|||||||
var openChannel = function (ctx, teamData, id, _cb) {
|
var openChannel = function (ctx, teamData, id, _cb) {
|
||||||
var cb = Util.once(_cb);
|
var cb = Util.once(_cb);
|
||||||
|
|
||||||
var secret = Hash.getSecrets('team', teamData.hash, teamData.password);
|
var hash = teamData.hash || teamData.roHash;
|
||||||
|
var secret = Hash.getSecrets('team', hash, teamData.password);
|
||||||
var crypto = Crypto.createEncryptor(secret.keys);
|
var crypto = Crypto.createEncryptor(secret.keys);
|
||||||
|
|
||||||
|
if (!teamData.roHash) {
|
||||||
|
teamData.roHash = Hash.getViewHashFromKeys(secret);
|
||||||
|
}
|
||||||
|
|
||||||
var keys = teamData.keys;
|
var keys = teamData.keys;
|
||||||
|
if (!keys.chat.validateKey && keys.chat.edit) {
|
||||||
|
var chatSecret = Hash.getSecrets('chat', keys.chat.edit);
|
||||||
|
keys.chat.validateKey = chatSecret.keys.validateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var roster;
|
var roster;
|
||||||
var lm;
|
var lm;
|
||||||
@ -373,6 +392,7 @@ define([
|
|||||||
}, waitFor(function (err, _roster) {
|
}, waitFor(function (err, _roster) {
|
||||||
if (err) {
|
if (err) {
|
||||||
waitFor.abort();
|
waitFor.abort();
|
||||||
|
console.error(err);
|
||||||
return void cb({error: 'ROSTER_ERROR'});
|
return void cb({error: 'ROSTER_ERROR'});
|
||||||
}
|
}
|
||||||
roster = _roster;
|
roster = _roster;
|
||||||
@ -421,6 +441,7 @@ define([
|
|||||||
var password = Hash.createChannelId();
|
var password = Hash.createChannelId();
|
||||||
var hash = Hash.createRandomHash('team', password);
|
var hash = Hash.createRandomHash('team', password);
|
||||||
var secret = Hash.getSecrets('team', hash, password);
|
var secret = Hash.getSecrets('team', hash, password);
|
||||||
|
var roHash = Hash.getViewHashFromKeys(secret);
|
||||||
var keyPair = Nacl.sign.keyPair(); // keyPair.secretKey , keyPair.publicKey
|
var keyPair = Nacl.sign.keyPair(); // keyPair.secretKey , keyPair.publicKey
|
||||||
|
|
||||||
var rosterSeed = Crypto.Team.createSeed();
|
var rosterSeed = Crypto.Team.createSeed();
|
||||||
@ -520,6 +541,7 @@ define([
|
|||||||
chat: {
|
chat: {
|
||||||
edit: chatHashes.editHash,
|
edit: chatHashes.editHash,
|
||||||
view: chatHashes.viewHash,
|
view: chatHashes.viewHash,
|
||||||
|
validateKey: chatSecret.keys.validateKey,
|
||||||
channel: chatSecret.channel
|
channel: chatSecret.channel
|
||||||
},
|
},
|
||||||
roster: {
|
roster: {
|
||||||
@ -532,6 +554,7 @@ define([
|
|||||||
owner: true,
|
owner: true,
|
||||||
channel: secret.channel,
|
channel: secret.channel,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
|
roHash: roHash,
|
||||||
password: password,
|
password: password,
|
||||||
keys: keys,
|
keys: keys,
|
||||||
//members: membersHashes.editHash,
|
//members: membersHashes.editHash,
|
||||||
@ -665,7 +688,7 @@ define([
|
|||||||
|
|
||||||
var joinTeam = function (ctx, data, cId, cb) {
|
var joinTeam = function (ctx, data, cId, cb) {
|
||||||
var team = data.team;
|
var team = data.team;
|
||||||
if (!team.hash || !team.channel || !team.password
|
if (!(team.hash || team.roHash) || !team.channel || !team.password
|
||||||
|| !team.keys || !team.metadata) { return void cb({error: 'EINVAL'}); }
|
|| !team.keys || !team.metadata) { return void cb({error: 'EINVAL'}); }
|
||||||
var id = Util.createRandomInteger();
|
var id = Util.createRandomInteger();
|
||||||
ctx.store.proxy.teams[id] = team;
|
ctx.store.proxy.teams[id] = team;
|
||||||
@ -924,6 +947,92 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var getInviteData = function (ctx, teamId, edit) {
|
||||||
|
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
|
||||||
|
if (!teamData) { return {}; }
|
||||||
|
var data = Util.clone(teamData);
|
||||||
|
if (!edit) {
|
||||||
|
// Delete edit keys
|
||||||
|
delete data.hash;
|
||||||
|
delete data.keys.drive.edPrivate;
|
||||||
|
delete data.keys.chat.edit;
|
||||||
|
}
|
||||||
|
// Delete owner key
|
||||||
|
delete data.owner;
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update my edit rights in listmap (only upgrade) and userObject (upgrade and downgrade)
|
||||||
|
// We also need to propagate the changes to the shared folders
|
||||||
|
var updateMyRights = function (ctx, teamId, hash) {
|
||||||
|
if (!teamId) { return true; }
|
||||||
|
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
|
||||||
|
if (!teamData) { return true; }
|
||||||
|
var team = ctx.teams[teamId];
|
||||||
|
|
||||||
|
var secret = Hash.getSecrets('team', hash || teamData.roHash, teamData.password);
|
||||||
|
// Upgrade the listmap if we can
|
||||||
|
SF.upgrade(teamData.channel, secret);
|
||||||
|
// Set the new readOnly value in userObject
|
||||||
|
if (team.userObject) {
|
||||||
|
team.userObject.setReadOnly(!secret.keys.secondaryKey, secret.keys.secondaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade the shared folders
|
||||||
|
var folders = Util.find(team, ['proxy', 'drive', 'sharedFolders']);
|
||||||
|
Object.keys(folders || {}).forEach(function (sfId) {
|
||||||
|
var data = team.manager.getSharedFolderData(sfId);
|
||||||
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||||
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||||
|
SF.upgrade(secret.channel, secret);
|
||||||
|
var uo = Util.find(team, ['manager', 'folders', sfId, 'userObject']);
|
||||||
|
if (uo) {
|
||||||
|
uo.setReadOnly(!secret.keys.secondaryKey, secret.keys.secondaryKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.emit('ROSTER_CHANGE_RIGHTS', teamId, team.clients);
|
||||||
|
};
|
||||||
|
|
||||||
|
var changeMyRights = function (ctx, teamId, state, data) {
|
||||||
|
if (!teamId) { return true; }
|
||||||
|
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
|
||||||
|
if (!teamData) { return true; }
|
||||||
|
var team = ctx.teams[teamId];
|
||||||
|
if (!team) { return true; }
|
||||||
|
|
||||||
|
if (teamData.channel !== data.channel || teamData.password !== data.password) { return true; }
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
teamData.hash = data.hash;
|
||||||
|
teamData.keys.drive.edPrivate = data.keys.drive.edPrivate;
|
||||||
|
teamData.keys.chat.edit = data.keys.chat.edit;
|
||||||
|
} else {
|
||||||
|
delete teamData.hash;
|
||||||
|
delete teamData.keys.drive.edPrivate;
|
||||||
|
delete teamData.keys.chat.edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMyRights(ctx, teamId, data.hash);
|
||||||
|
};
|
||||||
|
var changeEditRights = function (ctx, teamId, user, state, cb) {
|
||||||
|
if (!teamId) { return void cb({error: 'EINVAL'}); }
|
||||||
|
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
|
||||||
|
if (!teamData) { return void cb ({error: 'ENOENT'}); }
|
||||||
|
var team = ctx.teams[teamId];
|
||||||
|
if (!team) { return void cb ({error: 'ENOENT'}); }
|
||||||
|
|
||||||
|
// Send mailbox to offer ownership
|
||||||
|
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||||
|
ctx.store.mailbox.sendTo("TEAM_EDIT_RIGHTS", {
|
||||||
|
state: state,
|
||||||
|
teamData: getInviteData(ctx, teamId, state),
|
||||||
|
user: myData
|
||||||
|
}, {
|
||||||
|
channel: user.notifications,
|
||||||
|
curvePublic: user.curvePublic
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
var describeUser = function (ctx, data, cId, cb) {
|
var describeUser = function (ctx, data, cId, cb) {
|
||||||
var teamId = data.teamId;
|
var teamId = data.teamId;
|
||||||
if (!teamId) { return void cb({error: 'EINVAL'}); }
|
if (!teamId) { return void cb({error: 'EINVAL'}); }
|
||||||
@ -937,13 +1046,27 @@ define([
|
|||||||
// It it is an ownership revocation, we have to set it in pad metadata first
|
// It it is an ownership revocation, we have to set it in pad metadata first
|
||||||
if (user.role === "OWNER" && data.data.role !== "OWNER") {
|
if (user.role === "OWNER" && data.data.role !== "OWNER") {
|
||||||
revokeOwnership(ctx, teamId, user, function (err) {
|
revokeOwnership(ctx, teamId, user, function (err) {
|
||||||
if (!err) { return; }
|
if (!err) { return void cb(); }
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return void cb({error: err});
|
return void cb({error: err});
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Viewer to editor
|
||||||
|
if (user.role === "VIEWER" && data.data.role !== "VIEWER") {
|
||||||
|
return void changeEditRights(ctx, teamId, user, true, function (err) {
|
||||||
|
return void cb({error: err});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editor to viewer
|
||||||
|
if (user.role !== "VIEWER" && data.data.role === "VIEWER") {
|
||||||
|
return void changeEditRights(ctx, teamId, user, false, function (err) {
|
||||||
|
return void cb({error: err});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj[data.curvePublic] = data.data;
|
obj[data.curvePublic] = data.data;
|
||||||
team.roster.describe(obj, function (err) {
|
team.roster.describe(obj, function (err) {
|
||||||
@ -952,15 +1075,6 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO send guest keys only in the future
|
|
||||||
var getInviteData = function (ctx, teamId) {
|
|
||||||
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
|
|
||||||
if (!teamData) { return {}; }
|
|
||||||
var data = Util.clone(teamData);
|
|
||||||
delete data.owner;
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
var inviteToTeam = function (ctx, data, cId, cb) {
|
var inviteToTeam = function (ctx, data, cId, cb) {
|
||||||
var teamId = data.teamId;
|
var teamId = data.teamId;
|
||||||
if (!teamId) { return void cb({error: 'EINVAL'}); }
|
if (!teamId) { return void cb({error: 'EINVAL'}); }
|
||||||
@ -975,6 +1089,7 @@ define([
|
|||||||
|
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj[user.curvePublic] = user;
|
obj[user.curvePublic] = user;
|
||||||
|
obj[user.curvePublic].role = 'VIEWER';
|
||||||
team.roster.add(obj, function (err) {
|
team.roster.add(obj, function (err) {
|
||||||
if (err && err !== 'NO_CHANGE') { return void cb({error: err}); }
|
if (err && err !== 'NO_CHANGE') { return void cb({error: err}); }
|
||||||
ctx.store.mailbox.sendTo('INVITE_TO_TEAM', {
|
ctx.store.mailbox.sendTo('INVITE_TO_TEAM', {
|
||||||
@ -1100,9 +1215,21 @@ define([
|
|||||||
if (err) { return; }
|
if (err) { return; }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Listen for changes in our access rights (if another worker receives edit access)
|
||||||
|
ctx.store.proxy.on('change', ['teams'], function (o, n, p) {
|
||||||
|
if (p[2] !== 'hash') { return; }
|
||||||
|
updateMyRights(ctx, p[1], n);
|
||||||
|
});
|
||||||
|
ctx.store.proxy.on('remove', ['teams'], function (o, p) {
|
||||||
|
if (p[2] !== 'hash') { return; }
|
||||||
|
updateMyRights(ctx, p[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Object.keys(teams).forEach(function (id) {
|
Object.keys(teams).forEach(function (id) {
|
||||||
ctx.onReadyHandlers[id] = [];
|
ctx.onReadyHandlers[id] = [];
|
||||||
openChannel(ctx, teams[id], id, waitFor(function () {
|
openChannel(ctx, teams[id], id, waitFor(function (err) {
|
||||||
|
if (err) { return void console.error(err); }
|
||||||
console.debug('Team '+id+' ready');
|
console.debug('Team '+id+' ready');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -1121,7 +1248,7 @@ define([
|
|||||||
edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']),
|
edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']),
|
||||||
avatar: Util.find(teams[id], ['metadata', 'avatar'])
|
avatar: Util.find(teams[id], ['metadata', 'avatar'])
|
||||||
};
|
};
|
||||||
if (safe) {
|
if (safe && ctx.teams[id]) {
|
||||||
t[id].secondaryKey = ctx.teams[id].secondaryKey;
|
t[id].secondaryKey = ctx.teams[id].secondaryKey;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1147,6 +1274,9 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
team.changeMyRights = function (id, edit, teamData) {
|
||||||
|
changeMyRights(ctx, id, edit, teamData);
|
||||||
|
};
|
||||||
team.updateMyData = function (data) {
|
team.updateMyData = function (data) {
|
||||||
Object.keys(ctx.teams).forEach(function (id) {
|
Object.keys(ctx.teams).forEach(function (id) {
|
||||||
var team = ctx.teams[id];
|
var team = ctx.teams[id];
|
||||||
|
|||||||
@ -21,6 +21,8 @@ define([
|
|||||||
var sharedFolder = config.sharedFolder;
|
var sharedFolder = config.sharedFolder;
|
||||||
var edPublic = config.edPublic;
|
var edPublic = config.edPublic;
|
||||||
|
|
||||||
|
var readOnly = config.readOnly;
|
||||||
|
|
||||||
var ROOT = exp.ROOT;
|
var ROOT = exp.ROOT;
|
||||||
var FILES_DATA = exp.FILES_DATA;
|
var FILES_DATA = exp.FILES_DATA;
|
||||||
var OLD_FILES_DATA = exp.OLD_FILES_DATA;
|
var OLD_FILES_DATA = exp.OLD_FILES_DATA;
|
||||||
@ -31,8 +33,14 @@ define([
|
|||||||
|
|
||||||
var debug = exp.debug;
|
var debug = exp.debug;
|
||||||
|
|
||||||
|
exp._setReadOnly = function (state) {
|
||||||
|
readOnly = state;
|
||||||
|
if (!readOnly) { exp.fixFiles(); }
|
||||||
|
};
|
||||||
|
|
||||||
exp.setHref = function (channel, id, href) {
|
exp.setHref = function (channel, id, href) {
|
||||||
if (!id && !channel) { return; }
|
if (!id && !channel) { return; }
|
||||||
|
if (readOnly) { return; }
|
||||||
var ids = id ? [id] : exp.findChannels([channel]);
|
var ids = id ? [id] : exp.findChannels([channel]);
|
||||||
ids.forEach(function (i) {
|
ids.forEach(function (i) {
|
||||||
var data = exp.getFileData(i, true);
|
var data = exp.getFileData(i, true);
|
||||||
@ -42,6 +50,7 @@ define([
|
|||||||
|
|
||||||
exp.setPadAttribute = function (href, attr, value, cb) {
|
exp.setPadAttribute = function (href, attr, value, cb) {
|
||||||
cb = cb || function () {};
|
cb = cb || function () {};
|
||||||
|
if (readOnly) { return void cb('EFORBIDDEN'); }
|
||||||
var id = exp.getIdFromHref(href);
|
var id = exp.getIdFromHref(href);
|
||||||
if (!id) { return void cb("E_INVAL_HREF"); }
|
if (!id) { return void cb("E_INVAL_HREF"); }
|
||||||
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
|
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
|
||||||
@ -63,6 +72,7 @@ define([
|
|||||||
|
|
||||||
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'); }
|
||||||
var id = Util.createRandomInteger();
|
var id = Util.createRandomInteger();
|
||||||
// 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); }
|
||||||
@ -72,12 +82,21 @@ define([
|
|||||||
|
|
||||||
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'); }
|
||||||
|
|
||||||
// Check if we already have this shared folder in our drive
|
// Check if we already have this shared folder in our drive
|
||||||
|
var exists;
|
||||||
if (Object.keys(files[SHARED_FOLDERS]).some(function (k) {
|
if (Object.keys(files[SHARED_FOLDERS]).some(function (k) {
|
||||||
return files[SHARED_FOLDERS][k].channel === data.channel;
|
if (files[SHARED_FOLDERS][k].channel === data.channel) {
|
||||||
|
// We already know this shared folder. Check if we can get better access rights
|
||||||
|
if (data.href && !files[SHARED_FOLDERS][k].href) {
|
||||||
|
files[SHARED_FOLDERS][k].href = data.href;
|
||||||
|
}
|
||||||
|
exists = k;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
})) {
|
})) {
|
||||||
return void cb ('EEXISTS');
|
return void cb ('EEXISTS', exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the folder
|
// Add the folder
|
||||||
@ -92,6 +111,7 @@ define([
|
|||||||
|
|
||||||
// FILES DATA
|
// FILES DATA
|
||||||
var spliceFileData = function (id) {
|
var spliceFileData = function (id) {
|
||||||
|
if (readOnly) { return; }
|
||||||
delete files[FILES_DATA][id];
|
delete files[FILES_DATA][id];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,6 +119,7 @@ define([
|
|||||||
// FILES_DATA. If there are owned pads, remove them from server too.
|
// FILES_DATA. If there are owned pads, remove them from server too.
|
||||||
exp.checkDeletedFiles = function (cb) {
|
exp.checkDeletedFiles = function (cb) {
|
||||||
if (!loggedIn && !config.testMode) { return void cb(); }
|
if (!loggedIn && !config.testMode) { return void cb(); }
|
||||||
|
if (readOnly) { return void cb('EFORBIDDEN'); }
|
||||||
|
|
||||||
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
||||||
var toClean = [];
|
var toClean = [];
|
||||||
@ -144,21 +165,22 @@ define([
|
|||||||
cb(null, toClean, ownedRemoved);
|
cb(null, toClean, ownedRemoved);
|
||||||
};
|
};
|
||||||
var deleteHrefs = function (ids) {
|
var deleteHrefs = function (ids) {
|
||||||
|
if (readOnly) { return; }
|
||||||
ids.forEach(function (obj) {
|
ids.forEach(function (obj) {
|
||||||
var idx = files[obj.root].indexOf(obj.id);
|
var idx = files[obj.root].indexOf(obj.id);
|
||||||
files[obj.root].splice(idx, 1);
|
files[obj.root].splice(idx, 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var deleteMultipleTrashRoot = function (roots) {
|
var deleteMultipleTrashRoot = function (roots) {
|
||||||
|
if (readOnly) { return; }
|
||||||
roots.forEach(function (obj) {
|
roots.forEach(function (obj) {
|
||||||
var idx = files[TRASH][obj.name].indexOf(obj.el);
|
var idx = files[TRASH][obj.name].indexOf(obj.el);
|
||||||
files[TRASH][obj.name].splice(idx, 1);
|
files[TRASH][obj.name].splice(idx, 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
exp.deleteMultiplePermanently = function (paths, nocheck, cb) {
|
exp.deleteMultiplePermanently = function (paths, nocheck, cb) {
|
||||||
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
|
if (readOnly) { return void cb('EFORBIDDEN'); }
|
||||||
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
|
|
||||||
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
|
|
||||||
var allFilesPaths = paths.filter(function(x) { return exp.isPathIn(x, [FILES_DATA]); });
|
var allFilesPaths = paths.filter(function(x) { return exp.isPathIn(x, [FILES_DATA]); });
|
||||||
|
|
||||||
if (!loggedIn && !config.testMode) {
|
if (!loggedIn && !config.testMode) {
|
||||||
@ -170,6 +192,10 @@ define([
|
|||||||
return void cb();
|
return void cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
|
||||||
|
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
|
||||||
|
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
|
||||||
|
|
||||||
var ids = [];
|
var ids = [];
|
||||||
hrefPaths.forEach(function (path) {
|
hrefPaths.forEach(function (path) {
|
||||||
var id = exp.find(path);
|
var id = exp.find(path);
|
||||||
@ -216,6 +242,7 @@ define([
|
|||||||
|
|
||||||
// From another drive
|
// From another drive
|
||||||
exp.copyFromOtherDrive = function (path, element, data, key) {
|
exp.copyFromOtherDrive = function (path, element, data, key) {
|
||||||
|
if (readOnly) { return; }
|
||||||
// Copy files data
|
// Copy files data
|
||||||
// We have to remove pads that are already in the current proxy to make sure
|
// We have to remove pads that are already in the current proxy to make sure
|
||||||
// we won't create duplicates
|
// we won't create duplicates
|
||||||
@ -275,6 +302,8 @@ define([
|
|||||||
|
|
||||||
// From the same drive
|
// From the same drive
|
||||||
var pushToTrash = function (name, element, path) {
|
var pushToTrash = function (name, element, path) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
var trash = files[TRASH];
|
var trash = files[TRASH];
|
||||||
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
|
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
|
||||||
var trashArray = trash[name];
|
var trashArray = trash[name];
|
||||||
@ -285,6 +314,7 @@ define([
|
|||||||
trashArray.push(trashElement);
|
trashArray.push(trashElement);
|
||||||
};
|
};
|
||||||
exp.copyElement = function (elementPath, newParentPath) {
|
exp.copyElement = function (elementPath, newParentPath) {
|
||||||
|
if (readOnly) { return; }
|
||||||
if (exp.comparePath(elementPath, newParentPath)) { return; } // Nothing to do...
|
if (exp.comparePath(elementPath, newParentPath)) { return; } // Nothing to do...
|
||||||
var element = exp.find(elementPath);
|
var element = exp.find(elementPath);
|
||||||
var newParent = exp.find(newParentPath);
|
var newParent = exp.find(newParentPath);
|
||||||
@ -332,6 +362,8 @@ define([
|
|||||||
|
|
||||||
// FORGET (move with href not path)
|
// FORGET (move with href not path)
|
||||||
exp.forget = function (href) {
|
exp.forget = function (href) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
var id = exp.getIdFromHref(href);
|
var id = exp.getIdFromHref(href);
|
||||||
if (!id) { return; }
|
if (!id) { return; }
|
||||||
if (!loggedIn && !config.testMode) {
|
if (!loggedIn && !config.testMode) {
|
||||||
@ -348,6 +380,8 @@ define([
|
|||||||
// If all the occurences of an href are in the trash, remove them and add the file in root.
|
// If all the occurences of an href are in the trash, remove them and add the file in root.
|
||||||
// This is use with setPadTitle when we open a stronger version of a deleted pad
|
// This is use with setPadTitle when we open a stronger version of a deleted pad
|
||||||
exp.restoreHref = function (href) {
|
exp.restoreHref = function (href) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
var idO = exp.getIdFromHref(href);
|
var idO = exp.getIdFromHref(href);
|
||||||
|
|
||||||
if (!idO || !exp.isFile(idO)) { return; }
|
if (!idO || !exp.isFile(idO)) { return; }
|
||||||
@ -370,6 +404,8 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.add = function (id, path) {
|
exp.add = function (id, path) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
if (!loggedIn && !config.testMode) { return; }
|
if (!loggedIn && !config.testMode) { return; }
|
||||||
id = Number(id);
|
id = Number(id);
|
||||||
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
|
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
|
||||||
@ -397,6 +433,8 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.setFolderData = function (path, key, value, cb) {
|
exp.setFolderData = function (path, key, value, cb) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
var folder = exp.find(path);
|
var folder = exp.find(path);
|
||||||
if (!exp.isFolder(folder) || exp.isSharedFolder(folder)) { return; }
|
if (!exp.isFolder(folder) || exp.isSharedFolder(folder)) { return; }
|
||||||
if (!exp.hasFolderData(folder)) {
|
if (!exp.hasFolderData(folder)) {
|
||||||
@ -423,7 +461,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.migrateReadOnly = function (cb) {
|
exp.migrateReadOnly = function (cb) {
|
||||||
if (!config.editKey) { return void cb({error: 'EFORBIDDEN'}); }
|
if (readOnly || !config.editKey) { return void cb({error: 'EFORBIDDEN'}); }
|
||||||
if (files.version >= 2) { return void cb(); } // Already migrated, nothing to do
|
if (files.version >= 2) { return void cb(); } // Already migrated, nothing to do
|
||||||
files.migrateRo = 1;
|
files.migrateRo = 1;
|
||||||
var next = function () {
|
var next = function () {
|
||||||
@ -453,6 +491,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.migrate = function (cb) {
|
exp.migrate = function (cb) {
|
||||||
|
if (readOnly) { return void cb(); }
|
||||||
// Make sure unsorted doesn't exist anymore
|
// Make sure unsorted doesn't exist anymore
|
||||||
// Note: Unsorted only works with the old structure where pads are href
|
// Note: Unsorted only works with the old structure where pads are href
|
||||||
// It should be called before the migration code
|
// It should be called before the migration code
|
||||||
@ -551,6 +590,9 @@ define([
|
|||||||
// - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
|
// - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
|
||||||
// * TEMPLATE: Contains only files (href), and does not contains files that are in ROOT
|
// * TEMPLATE: Contains only files (href), and does not contains files that are in ROOT
|
||||||
|
|
||||||
|
// We can't fix anything in read-only mode: abort
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
if (silent) { debug = function () {}; }
|
if (silent) { debug = function () {}; }
|
||||||
|
|
||||||
var t0 = +new Date();
|
var t0 = +new Date();
|
||||||
|
|||||||
@ -2,9 +2,10 @@ define([
|
|||||||
'/common/userObject.js',
|
'/common/userObject.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
'/common/common-hash.js',
|
'/common/common-hash.js',
|
||||||
|
'/common/outer/sharedfolder.js',
|
||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
], function (UserObject, Util, Hash, Messages, nThen) {
|
], function (UserObject, Util, Hash, SF, Messages, nThen) {
|
||||||
|
|
||||||
|
|
||||||
var getConfig = function (Env) {
|
var getConfig = function (Env) {
|
||||||
@ -20,6 +21,7 @@ define([
|
|||||||
cfg.id = id;
|
cfg.id = id;
|
||||||
cfg.editKey = editKey;
|
cfg.editKey = editKey;
|
||||||
cfg.rt = lm.realtime;
|
cfg.rt = lm.realtime;
|
||||||
|
cfg.readOnly = Boolean(!editKey);
|
||||||
var userObject = UserObject.init(lm.proxy, cfg);
|
var userObject = UserObject.init(lm.proxy, cfg);
|
||||||
if (userObject.fixFiles) {
|
if (userObject.fixFiles) {
|
||||||
// Only in outer
|
// Only in outer
|
||||||
@ -452,6 +454,12 @@ define([
|
|||||||
// 1. add the shared folder to our list of shared folders
|
// 1. add the shared folder to our list of shared folders
|
||||||
// NOTE: pushSharedFolder will encrypt the href directly in the object if needed
|
// NOTE: pushSharedFolder will encrypt the href directly in the object if needed
|
||||||
Env.user.userObject.pushSharedFolder(folderData, waitFor(function (err, folderId) {
|
Env.user.userObject.pushSharedFolder(folderData, waitFor(function (err, folderId) {
|
||||||
|
if (err === "EEXISTS" && folderData.href && folderId) {
|
||||||
|
var parsed = Hash.parsePadUrl(folderData.href);
|
||||||
|
var secret = Hash.getSecrets('drive', parsed.hash, folderData.password);
|
||||||
|
SF.upgrade(secret.channel, secret);
|
||||||
|
Env.folders[folderId].userObject.setReadOnly(false, secret.keys.secondaryKey);
|
||||||
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
waitFor.abort();
|
waitFor.abort();
|
||||||
return void cb(err);
|
return void cb(err);
|
||||||
|
|||||||
@ -32,13 +32,15 @@ define([
|
|||||||
module.init = function (files, config) {
|
module.init = function (files, config) {
|
||||||
var exp = {};
|
var exp = {};
|
||||||
|
|
||||||
exp.cryptor = {
|
exp.cryptor = {};
|
||||||
encrypt : function (x) { return x; },
|
var createCryptor = function (key) {
|
||||||
decrypt : function (x) { return x; },
|
if (!key) {
|
||||||
};
|
exp.cryptor.encrypt = function (x) { return x; };
|
||||||
if (config.editKey) {
|
exp.cryptor.decrypt = function (x) { return x; };
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var c = Crypto.createEncryptor(config.editKey);
|
var c = Crypto.createEncryptor(key);
|
||||||
exp.cryptor.encrypt = function (href) {
|
exp.cryptor.encrypt = function (href) {
|
||||||
// Never encrypt blob href, they are always read-only
|
// Never encrypt blob href, they are always read-only
|
||||||
if (href.slice(0,7) === '/file/#') { return href; }
|
if (href.slice(0,7) === '/file/#') { return href; }
|
||||||
@ -48,7 +50,17 @@ define([
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
createCryptor(config.editKey);
|
||||||
|
|
||||||
|
exp.setReadOnly = function (state, key) {
|
||||||
|
config.editKey = key;
|
||||||
|
createCryptor(key);
|
||||||
|
if (exp._setReadOnly) {
|
||||||
|
// Change outer
|
||||||
|
exp._setReadOnly(state);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
exp.getDefaultName = module.getDefaultName;
|
exp.getDefaultName = module.getDefaultName;
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ define([
|
|||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
Object.keys(drive.sharedFolders).forEach(function (fId) {
|
Object.keys(drive.sharedFolders).forEach(function (fId) {
|
||||||
var sfData = drive.sharedFolders[fId] || {};
|
var sfData = drive.sharedFolders[fId] || {};
|
||||||
var parsed = Hash.parsePadUrl(sfData.href);
|
var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref);
|
||||||
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
||||||
sframeChan.query('Q_DRIVE_GETOBJECT', {
|
sframeChan.query('Q_DRIVE_GETOBJECT', {
|
||||||
sharedFolder: fId
|
sharedFolder: fId
|
||||||
@ -54,6 +54,9 @@ define([
|
|||||||
if (manager && oldIds.indexOf(fId) === -1) {
|
if (manager && oldIds.indexOf(fId) === -1) {
|
||||||
manager.addProxy(fId, { proxy: folders[fId] }, null, secret.keys.secondaryKey);
|
manager.addProxy(fId, { proxy: folders[fId] }, null, secret.keys.secondaryKey);
|
||||||
}
|
}
|
||||||
|
var readOnly = !secret.keys.editKeyStr;
|
||||||
|
if (!manager || !manager.folders[fId]) { return; }
|
||||||
|
manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
|
|||||||
@ -53,7 +53,7 @@ define([
|
|||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
Object.keys(drive.sharedFolders).forEach(function (fId) {
|
Object.keys(drive.sharedFolders).forEach(function (fId) {
|
||||||
var sfData = drive.sharedFolders[fId] || {};
|
var sfData = drive.sharedFolders[fId] || {};
|
||||||
var parsed = Hash.parsePadUrl(sfData.href);
|
var parsed = Hash.parsePadUrl(sfData.href || sfData.roHref);
|
||||||
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
var secret = Hash.getSecrets('drive', parsed.hash, sfData.password);
|
||||||
sframeChan.query('Q_DRIVE_GETOBJECT', {
|
sframeChan.query('Q_DRIVE_GETOBJECT', {
|
||||||
sharedFolder: fId
|
sharedFolder: fId
|
||||||
@ -467,7 +467,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var ROLES = ['MEMBER', 'ADMIN', 'OWNER'];
|
var ROLES = ['VIEWER', 'MEMBER', 'ADMIN', 'OWNER'];
|
||||||
var describeUser = function (common, curvePublic, data, icon) {
|
var describeUser = function (common, curvePublic, data, icon) {
|
||||||
APP.module.execCommand('DESCRIBE_USER', {
|
APP.module.execCommand('DESCRIBE_USER', {
|
||||||
teamId: APP.team,
|
teamId: APP.team,
|
||||||
@ -505,10 +505,11 @@ define([
|
|||||||
var actions = h('span.cp-team-member-actions');
|
var actions = h('span.cp-team-member-actions');
|
||||||
var $actions = $(actions);
|
var $actions = $(actions);
|
||||||
var isMe = me && me.curvePublic === data.curvePublic;
|
var isMe = me && me.curvePublic === data.curvePublic;
|
||||||
var myRole = me ? (ROLES.indexOf(me.role) || 0) : -1;
|
var myRole = me ? (ROLES.indexOf(me.role) || 1) : -1;
|
||||||
var theirRole = ROLES.indexOf(data.role) || 0;
|
var theirRole = ROLES.indexOf(data.role) || 1;
|
||||||
|
var ADMIN = ROLES.indexOf('ADMIN');
|
||||||
// If they're an admin and I am an owner, I can promote them to owner
|
// If they're an admin and I am an owner, I can promote them to owner
|
||||||
if (!isMe && myRole > theirRole && theirRole === 1 && !data.pending) {
|
if (!isMe && myRole > theirRole && theirRole === ADMIN && !data.pending) {
|
||||||
var promoteOwner = h('span.fa.fa-angle-double-up', {
|
var promoteOwner = h('span.fa.fa-angle-double-up', {
|
||||||
title: Messages.team_rosterPromoteOwner
|
title: Messages.team_rosterPromoteOwner
|
||||||
});
|
});
|
||||||
@ -530,28 +531,28 @@ define([
|
|||||||
});
|
});
|
||||||
$actions.append(promoteOwner);
|
$actions.append(promoteOwner);
|
||||||
}
|
}
|
||||||
// If they're a member and I have a higher role than them, I can promote them to admin
|
// If they're a viewer/member and I have a higher role than them, I can promote them to admin
|
||||||
if (!isMe && myRole > theirRole && theirRole === 0 && !data.pending) {
|
if (!isMe && myRole >= ADMIN && theirRole < ADMIN && !data.pending) {
|
||||||
var promote = h('span.fa.fa-angle-double-up', {
|
var promote = h('span.fa.fa-angle-double-up', {
|
||||||
title: Messages.team_rosterPromote
|
title: Messages.team_rosterPromote
|
||||||
});
|
});
|
||||||
$(promote).click(function () {
|
$(promote).click(function () {
|
||||||
$(promote).hide();
|
$(promote).hide();
|
||||||
describeUser(common, data.curvePublic, {
|
describeUser(common, data.curvePublic, {
|
||||||
role: 'ADMIN'
|
role: ROLES[theirRole + 1]
|
||||||
}, promote);
|
}, promote);
|
||||||
});
|
});
|
||||||
$actions.append(promote);
|
$actions.append(promote);
|
||||||
}
|
}
|
||||||
// If I'm not a member and I have an equal or higher role than them, I can demote them
|
// If I'm not a member and I have an equal or higher role than them, I can demote them
|
||||||
// (if they're not already a MEMBER)
|
// (if they're not already a MEMBER)
|
||||||
if (myRole >= theirRole && theirRole > 0 && !data.pending) {
|
if (myRole >= theirRole && myRole >= ADMIN && theirRole > 0 && !data.pending) {
|
||||||
var demote = h('span.fa.fa-angle-double-down', {
|
var demote = h('span.fa.fa-angle-double-down', {
|
||||||
title: Messages.team_rosterDemote
|
title: Messages.team_rosterDemote
|
||||||
});
|
});
|
||||||
$(demote).click(function () {
|
$(demote).click(function () {
|
||||||
var todo = function () {
|
var todo = function () {
|
||||||
var role = ROLES[theirRole - 1] || 'MEMBER';
|
var role = ROLES[theirRole - 1] || 'VIEWER';
|
||||||
$(demote).hide();
|
$(demote).hide();
|
||||||
describeUser(common, data.curvePublic, {
|
describeUser(common, data.curvePublic, {
|
||||||
role: role
|
role: role
|
||||||
@ -569,9 +570,9 @@ define([
|
|||||||
$actions.append(demote);
|
$actions.append(demote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If I'm not a member and I have an equal or higher role than them, I can remove them
|
// If I'm at least an admin and I have an equal or higher role than them, I can remove them
|
||||||
// Note: we can't remove owners, we have to demote them first
|
// Note: we can't remove owners, we have to demote them first
|
||||||
if (!isMe && myRole > 0 && myRole >= theirRole && theirRole !== 2) {
|
if (!isMe && myRole >= ADMIN && myRole >= theirRole && theirRole !== ROLES.indexOf('OWNER')) {
|
||||||
var remove = h('span.fa.fa-times', {
|
var remove = h('span.fa.fa-times', {
|
||||||
title: Messages.team_rosterKick
|
title: Messages.team_rosterKick
|
||||||
});
|
});
|
||||||
@ -637,6 +638,12 @@ define([
|
|||||||
}).map(function (k) {
|
}).map(function (k) {
|
||||||
return makeMember(common, roster[k], me);
|
return makeMember(common, roster[k], me);
|
||||||
});
|
});
|
||||||
|
var viewers = Object.keys(roster).filter(function (k) {
|
||||||
|
if (roster[k].pending) { return; }
|
||||||
|
return roster[k].role === "VIEWER";
|
||||||
|
}).map(function (k) {
|
||||||
|
return makeMember(common, roster[k], me);
|
||||||
|
});
|
||||||
var pending = Object.keys(roster).filter(function (k) {
|
var pending = Object.keys(roster).filter(function (k) {
|
||||||
if (!roster[k].pending) { return; }
|
if (!roster[k].pending) { return; }
|
||||||
return roster[k].role === "MEMBER" || !roster[k].role;
|
return roster[k].role === "MEMBER" || !roster[k].role;
|
||||||
@ -671,7 +678,7 @@ define([
|
|||||||
$header.append(invite);
|
$header.append(invite);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me && (me.role === 'ADMIN' || me.role === 'MEMBER')) {
|
if (me && (me.role !== 'OWNER')) {
|
||||||
var leave = h('button.btn.btn-danger', Messages.team_leaveButton);
|
var leave = h('button.btn.btn-danger', Messages.team_leaveButton);
|
||||||
$(leave).click(function () {
|
$(leave).click(function () {
|
||||||
UI.confirm(Messages.team_leaveConfirm, function (yes) {
|
UI.confirm(Messages.team_leaveConfirm, function (yes) {
|
||||||
@ -698,6 +705,8 @@ define([
|
|||||||
h('div', admins),
|
h('div', admins),
|
||||||
h('h3', Messages.team_members),
|
h('h3', Messages.team_members),
|
||||||
h('div', members),
|
h('div', members),
|
||||||
|
h('h3', Messages.team_viewers || 'VIEWERS'), // XXX
|
||||||
|
h('div', viewers),
|
||||||
h('h3'+noPending, Messages.team_pending),
|
h('h3'+noPending, Messages.team_pending),
|
||||||
h('div'+noPending, pending)
|
h('div'+noPending, pending)
|
||||||
];
|
];
|
||||||
@ -721,7 +730,8 @@ define([
|
|||||||
common.setTeamChat(obj.channel);
|
common.setTeamChat(obj.channel);
|
||||||
MessengerUI.create($(container), common, {
|
MessengerUI.create($(container), common, {
|
||||||
chat: $('.cp-team-cat-chat'),
|
chat: $('.cp-team-cat-chat'),
|
||||||
team: true
|
team: true,
|
||||||
|
readOnly: obj.readOnly
|
||||||
});
|
});
|
||||||
cb(content);
|
cb(content);
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user