Merge branch 'staging' into soon

pull/1/head
ansuz 4 years ago
commit f84c5f9f66

@ -736,14 +736,20 @@ define([
UI.proposal = function (content, cb) { UI.proposal = function (content, cb) {
var clicked = false;
var buttons = [{ var buttons = [{
name: Messages.friendRequest_later, name: Messages.friendRequest_later,
onClick: function () {}, onClick: function () {
if (clicked) { return; }
clicked = true;
},
keys: [27] keys: [27]
}, { }, {
className: 'primary', className: 'primary',
name: Messages.friendRequest_accept, name: Messages.friendRequest_accept,
onClick: function () { onClick: function () {
if (clicked) { return; }
clicked = true;
cb(true); cb(true);
}, },
keys: [13] keys: [13]
@ -751,6 +757,8 @@ define([
className: 'primary', className: 'primary',
name: Messages.friendRequest_decline, name: Messages.friendRequest_decline,
onClick: function () { onClick: function () {
if (clicked) { return; }
clicked = true;
cb(false); cb(false);
}, },
keys: [[13, 'ctrl']] keys: [[13, 'ctrl']]

@ -56,6 +56,16 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
return ['OWNER', 'ADMIN', 'MEMBER', 'VIEWER'].indexOf(role) !== -1; return ['OWNER', 'ADMIN', 'MEMBER', 'VIEWER'].indexOf(role) !== -1;
}; };
var isSelfDowngrade = function (author, curve, role, state) {
// Make sure you want to describe yourself
var selfDescribe = author === curve && state[curve];
if (!selfDescribe) { return false; }
// ADMIN and OWNER can always update roles
// we only need to allow MEMBER to downgrade themselves to VIEWER
var authorRole = Util.find(state, [author, 'role']);
if (authorRole === "MEMBER") { return role === 'VIEWER'; }
};
var canAddRole = function (author, role, members) { var canAddRole = function (author, role, members) {
var authorRole = Util.find(members, [author, 'role']); var authorRole = Util.find(members, [author, 'role']);
if (!authorRole) { return false; } if (!authorRole) { return false; }
@ -244,7 +254,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
if (typeof(data.role) === 'string') { // they're trying to change the role... if (typeof(data.role) === 'string') { // they're trying to change the role...
// throw if they're trying to upgrade to something greater // throw if they're trying to upgrade to something greater
if (!canAddRole(author, data.role, members)) { throw new Error("INSUFFICIENT_PERMISSIONS"); } if (!isSelfDowngrade(author, curve, data.role, members) &&
!canAddRole(author, data.role, members)) {
throw new Error("INSUFFICIENT_PERMISSIONS");
}
} }
// DESCRIBE commands must initialize a displayName if it isn't already present // DESCRIBE commands must initialize a displayName if it isn't already present
if (typeof(current.displayName) !== 'string' && typeof(data.displayName) !== 'string') { if (typeof(current.displayName) !== 'string' && typeof(data.displayName) !== 'string') {

@ -491,6 +491,7 @@ define([
Feedback.send("ROSTER_CORRUPTED"); Feedback.send("ROSTER_CORRUPTED");
return; return;
} }
// Kicked from the team
if (!state.members[me]) { if (!state.members[me]) {
lm.stop(); lm.stop();
roster.stop(); roster.stop();
@ -499,6 +500,30 @@ define([
ctx.updateMetadata(); ctx.updateMetadata();
cb({error: 'EFORBIDDEN'}); cb({error: 'EFORBIDDEN'});
waitFor.abort(); waitFor.abort();
return;
}
// Check access rights
// If we're not a viewer, make sure we have edit rights
var s = state.members[me];
var teamEdPrivate = Util.find(teamData, ['keys', 'drive', 'edPrivate']);
if ((!teamData.hash || !teamEdPrivate) && ['ADMIN', 'MEMBER'].indexOf(s.role) !== -1) {
console.warn("Missing edit rights: demote to viewer");
var data = {};
data[ctx.store.proxy.curvePublic] = {
role: "VIEWER"
};
roster.describe(data, function (err) {
Feedback.send("TEAM_RIGHTS_FIXED");
// Make sure we've removed all the keys
delete teamData.hash;
delete teamData.keys.drive.edPrivate;
delete teamData.keys.chat.edit;
if (!err) { return; }
if (err === 'NO_CHANGE') { return; }
console.error(err);
});
} else if ((!teamData.hash || !teamEdPrivate) && s.role === "OWNER") {
Feedback.send("TEAM_RIGHTS_OWNER");
} }
}).nThen(function () { }).nThen(function () {
onReady(ctx, id, lm, roster, keys, null, cb); onReady(ctx, id, lm, roster, keys, null, cb);
@ -1122,6 +1147,20 @@ define([
var onReady = ctx.onReadyHandlers[teamId]; var onReady = ctx.onReadyHandlers[teamId];
var team = ctx.teams[teamId]; var team = ctx.teams[teamId];
if (teamData.channel !== data.channel || teamData.password !== data.password) { return void cb(false); }
// Update our proxy
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;
}
// Team not ready yet: try again onReady
if (!team && Array.isArray(onReady)) { if (!team && Array.isArray(onReady)) {
onReady.push({ onReady.push({
cb: function () { cb: function () {
@ -1131,14 +1170,11 @@ define([
return; return;
} }
// No team and not initialized at all...
if (!team) { return void cb(false); } if (!team) { return void cb(false); }
if (teamData.channel !== data.channel || teamData.password !== data.password) { return void cb(false); } // Team is initialized and ready: update the loaded elements
if (state) { if (state) {
teamData.hash = data.hash;
teamData.keys.drive.edPrivate = data.keys.drive.edPrivate;
teamData.keys.chat.edit = data.keys.chat.edit;
initRpc(ctx, team, teamData.keys.drive, function () { initRpc(ctx, team, teamData.keys.drive, function () {
team.manager.addPin(team.pin, team.unpin); team.manager.addPin(team.pin, team.unpin);
}); });
@ -1149,9 +1185,6 @@ define([
var crypto = Crypto.createEncryptor(secret.keys); var crypto = Crypto.createEncryptor(secret.keys);
team.listmap.setReadOnly(false, crypto); team.listmap.setReadOnly(false, crypto);
} else { } else {
delete teamData.hash;
delete teamData.keys.drive.edPrivate;
delete teamData.keys.chat.edit;
delete team.secondaryKey; delete team.secondaryKey;
if (team.rpc && team.rpc.destroy) { if (team.rpc && team.rpc.destroy) {
team.rpc.destroy(); team.rpc.destroy();
@ -1668,7 +1701,74 @@ define([
updateMyRights(ctx, p[1]); updateMyRights(ctx, p[1]);
}); });
var checkKeyPair = function (edPrivate, edPublic) {
if (!edPrivate || !edPublic) { return true; }
try {
var secretKey = Nacl.util.decodeBase64(edPrivate);
var pair = Nacl.sign.keyPair.fromSecretKey(secretKey);
return Nacl.util.encodeBase64(pair.publicKey) === edPublic;
} catch (e) {
return false;
}
};
// Remove duplicate teams
var _teams = {};
Object.keys(teams).forEach(function (id) {
try {
var t = teams[id];
var _t = _teams[t.channel];
var edPrivate = Util.find(t, ['keys', 'drive', 'edPrivate']);
var edPublic = Util.find(t, ['keys', 'drive', 'edPublic']);
// If the edPrivate is corrupted, remove it
if (!edPublic) {
Feedback.send("TEAM_CORRUPTED_EDPUBLIC");
} else if (edPrivate && edPublic && !checkKeyPair(edPrivate, edPublic)) {
Feedback.send("TEAM_CORRUPTED_EDPRIVATE");
delete teams[id].keys.drive.edPrivate;
edPrivate = undefined;
}
// If the hash is corrupted, feedback
if (t.hash) {
var parsed = Hash.parseTypeHash('drive', t.hash);
if (parsed.version === 2 && t.hash.length !== 40) {
Feedback.send("TEAM_CORRUPTED_HASH");
// FIXME ?
}
}
// Not found yet? add to the list
if (!_t) {
_teams[t.channel] = id;
return;
}
// Duplicate found: update our team to add missing data
var best = teams[_t]; // This is a proxy!
var bestPrivate = Util.find(best, ['keys', 'drive', 'edPrivate']);
var bestChat = Util.find(best, ['keys', 'chat', 'edit']);
var chat = Util.find(t, ['keys', 'chat', 'edit']);
if (!best.hash && t.hash) {
best.hash = t.hash;
}
if (!bestPrivate && edPrivate) {
best.keys.drive.edPrivate = edPrivate;
}
if (!bestChat && chat) {
best.keys.chat.edit = chat;
}
// Deprecate the duplicate
ctx.store.proxy.duplicateTeams = ctx.store.proxy.duplicateTeams || {};
ctx.store.proxy.duplicateTeams[id] = teams[id];
delete teams[id];
} catch (e) { console.error(e); }
});
// Load teams
Object.keys(teams).forEach(function (id) { Object.keys(teams).forEach(function (id) {
ctx.onReadyHandlers[id] = []; ctx.onReadyHandlers[id] = [];
if (!Util.find(teams, [id, 'keys', 'mailbox'])) { if (!Util.find(teams, [id, 'keys', 'mailbox'])) {
@ -1683,16 +1783,6 @@ define([
team.getTeam = function (id) { team.getTeam = function (id) {
return ctx.teams[id]; return ctx.teams[id];
}; };
var checkKeyPair = function (edPrivate, edPublic) {
if (!edPrivate || !edPublic) { return true; }
try {
var secretKey = Nacl.util.decodeBase64(edPrivate);
var pair = Nacl.sign.keyPair.fromSecretKey(secretKey);
return Nacl.util.encodeBase64(pair.publicKey) === edPublic;
} catch (e) {
return false;
}
};
team.getTeamsData = function (app) { team.getTeamsData = function (app) {
var t = {}; var t = {};
var safe = false; var safe = false;

@ -254,7 +254,7 @@ define([
// goal of having snapshots // goal of having snapshots
if (config.getLastMetadata) { if (config.getLastMetadata) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
var lastMd = config.getLastMetadata(); var lastMd = config.getLastMetadata() || {};
var _snapshots = lastMd.snapshots; var _snapshots = lastMd.snapshots;
var _users = lastMd.users; var _users = lastMd.users;
var md = Util.clone(metadataMgr.getMetadata()); var md = Util.clone(metadataMgr.getMetadata());

Loading…
Cancel
Save