cryptpad/www/common/outer/roster.js

651 lines
23 KiB
JavaScript
Raw Normal View History

2019-09-11 13:41:50 +00:00
(function () {
2019-09-17 09:28:22 +00:00
var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
2019-09-11 13:41:50 +00:00
var Roster = {};
2019-09-17 09:28:22 +00:00
/*
roster: {
state: {
2019-09-18 14:27:01 +00:00
members: {
user0CurveKey: {
notifications: "", // required
displayName: "", // required
role: "OWNER|ADMIN|MEMBER", // MEMBER if not specified
profile: "",
title: ""
},
user1CurveKey: {
...
}
2019-09-17 09:28:22 +00:00
},
2019-09-18 14:27:01 +00:00
metadata: {
// guaranteed to be strings, but may be empty
topic: '',
name: '',
avatar: '',
// anything else you use may not be defined
2019-09-17 09:28:22 +00:00
}
}
}
*/
2019-09-18 13:04:08 +00:00
var isMap = function (obj) {
return Boolean(obj && typeof(obj) === 'object' && !Array.isArray(obj));
};
2019-09-18 14:27:01 +00:00
var getMessageId = function (msgString) {
return msgString.slice(0, 64);
};
var canCheckpoint = function (author, members) {
2019-09-17 09:28:22 +00:00
// if you're here then you've received a checkpoint message
// that you don't necessarily trust.
// find the author's role from your knoweldge of the state
2019-09-18 14:27:01 +00:00
var role = Util.find(members, [author, 'role']);
2019-09-17 09:28:22 +00:00
// and check if it is 'OWNER' or 'ADMIN'
return ['OWNER', 'ADMIN'].indexOf(role) !== -1;
};
var isValidRole = function (role) {
return ['OWNER', 'ADMIN', 'MEMBER'].indexOf(role) !== -1;
};
2019-09-18 14:27:01 +00:00
var canAddRole = function (author, role, members) {
var authorRole = Util.find(members, [author, 'role']);
2019-09-17 09:28:22 +00:00
if (!authorRole) { return false; }
// nobody can add an invalid role
if (!isValidRole(role)) { return false; }
// owners can add any valid role they want
if (authorRole === 'OWNER') { return true; }
// admins can add other admins or members
if (authorRole === "ADMIN") { return ['ADMIN', 'MEMBER'].indexOf(role) !== -1; }
// (MEMBER, other) can't add anyone of any role
return false;
};
var isValidId = function (id) {
return typeof(id) === 'string' && id.length === 44;
};
var canDescribeTarget = function (author, curve, state) {
// you must be in the group to describe anyone
if (!state[curve]) { return false; }
// anyone can describe themself
if (author === curve && state[curve]) { return true; }
var authorRole = Util.find(state, [author, 'role']);
var targetRole = Util.find(state, [curve, 'role']);
// something is really wrong if there's no authorRole
if (!authorRole) { return false; }
// owners can do whatever they want
if (authorRole === 'OWNER') { return true; }
// admins can describe anyone escept owners
if (authorRole === 'ADMIN' && targetRole !== 'OWNER') { return true; }
// members can't describe others
return false;
};
2019-09-18 14:27:01 +00:00
var canRemoveRole = function (author, role, members) {
var authorRole = Util.find(members, [author, 'role']);
2019-09-17 09:28:22 +00:00
if (!authorRole) { return false; }
// owners can remove anyone they want
if (authorRole === 'OWNER') { return true; }
// admins can remove other admins or members
if (authorRole === "ADMIN") { return ["ADMIN", "MEMBER"].indexOf(role) !== -1; }
// MEMBERS and non-members cannot remove anyone of any role
return false;
};
2019-09-18 14:27:01 +00:00
var canUpdateMetadata = function (author, members) {
var authorRole = Util.find(members, [author, 'role']);
2019-09-18 13:04:08 +00:00
return Boolean(authorRole && ['OWNER', 'ADMIN'].indexOf(authorRole) !== -1);
};
2019-09-18 14:27:01 +00:00
var shouldCheckpoint = function (ref) {
ref = ref;
2019-09-17 09:28:22 +00:00
};
shouldCheckpoint = shouldCheckpoint; // XXX lint
var commands = Roster.commands = {};
/* Commands are functions with the signature
(args_any, base46_author_string, roster_map, optional_base64_message_id) => boolean
they:
* throw if any of their arguments are invalid
* return true if their application to previous state results in a change
* mutate the local account of the current state
changes to the state can be simulated locally before being sent.
if the simulation throws or returns false, don't send.
*/
// the author is trying to add someone to the roster
// owners can add any role
commands.ADD = function (args, author, roster) {
2019-09-18 14:27:01 +00:00
if (!isMap(args)) { throw new Error("INVALID ARGS"); }
if (!roster.internal.initialized) { throw new Error("UNITIALIZED"); }
if (typeof(roster.state.members) === 'undefined') {
2019-09-17 09:28:22 +00:00
throw new Error("CANNOT_ADD_TO_UNITIALIZED_ROSTER");
}
2019-09-18 14:27:01 +00:00
var members = roster.state.members;
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
// iterate over everything and make sure it is valid, throw if not
2019-09-17 09:28:22 +00:00
Object.keys(args).forEach(function (curve) {
// FIXME only allow valid curve keys, anything else is pollution
2019-09-18 13:04:08 +00:00
if (!isValidId(curve)) {
2019-09-17 09:28:22 +00:00
console.log(curve, curve.length);
throw new Error("INVALID_CURVE_KEY");
}
2019-09-18 13:04:08 +00:00
// reject commands where the members are not proper objects
if (!isMap(args[curve])) { throw new Error("INVALID_CONTENT"); }
2019-09-18 14:27:01 +00:00
if (members[curve]) { throw new Error("ALREADY_PRESENT"); }
2019-09-17 09:28:22 +00:00
var data = args[curve];
2019-09-18 13:04:08 +00:00
// if no role was provided, assume MEMBER
if (typeof(data.role) !== 'string') { data.role = 'MEMBER'; }
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
if (typeof(data.displayName) !== 'string') { throw new Error("DISPLAYNAME_REQUIRED"); }
if (typeof(data.notifications) !== 'string') { throw new Error("NOTIFICATIONS_REQUIRED"); }
});
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
var changed = false;
// then iterate again and apply it
Object.keys(args).forEach(function (curve) {
var data = args[curve];
2019-09-18 14:27:01 +00:00
if (!canAddRole(author, data.role, members)) { return; }
2019-09-17 09:28:22 +00:00
// this will result in a change
changed = true;
2019-09-18 14:27:01 +00:00
members[curve] = data;
2019-09-17 09:28:22 +00:00
});
return changed;
};
commands.RM = function (args, author, roster) {
if (!Array.isArray(args)) { throw new Error("INVALID_ARGS"); }
2019-09-18 14:27:01 +00:00
if (typeof(roster.state.members) === 'undefined') {
2019-09-17 09:28:22 +00:00
throw new Error("CANNOT_RM_FROM_UNITIALIZED_ROSTER");
}
2019-09-18 14:27:01 +00:00
var members = roster.state.members;
2019-09-17 09:28:22 +00:00
var changed = false;
args.forEach(function (curve) {
2019-09-18 13:04:08 +00:00
if (!isValidId(curve)) { throw new Error("INVALID_CURVE_KEY"); }
2019-09-17 09:28:22 +00:00
// don't try to remove something that isn't there
2019-09-18 14:27:01 +00:00
if (!members[curve]) { return; }
var role = members[curve].role;
if (!canRemoveRole(author, role, members)) { throw new Error("INSUFFICIENT_PERMISSIONS"); }
2019-09-17 09:28:22 +00:00
changed = true;
2019-09-18 14:27:01 +00:00
delete members[curve];
2019-09-17 09:28:22 +00:00
});
return changed;
};
commands.DESCRIBE = function (args, author, roster) {
if (!args || typeof(args) !== 'object' || Array.isArray(args)) {
throw new Error("INVALID_ARGUMENTS");
}
2019-09-18 14:27:01 +00:00
if (typeof(roster.state.members) === 'undefined') {
2019-09-18 13:04:08 +00:00
throw new Error("NOT_READY");
2019-09-17 09:28:22 +00:00
}
2019-09-18 14:27:01 +00:00
var members = roster.state.members;
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
// iterate over all the data and make sure it is valid, throw otherwise
2019-09-17 09:28:22 +00:00
Object.keys(args).forEach(function (curve) {
2019-09-18 13:04:08 +00:00
if (!isValidId(curve)) { throw new Error("INVALID_ID"); }
2019-09-18 14:27:01 +00:00
if (!members[curve]) { throw new Error("NOT_PRESENT"); }
2019-09-17 09:28:22 +00:00
2019-09-18 14:27:01 +00:00
if (!canDescribeTarget(author, curve, members)) { throw new Error("INSUFFICIENT_PERMISSIONS"); }
2019-09-18 13:04:08 +00:00
var data = args[curve];
if (!isMap(data)) { throw new Error("INVALID_ARGUMENTS"); }
2019-09-18 14:27:01 +00:00
var current = Util.clone(members[curve]);
2019-09-18 13:04:08 +00:00
// DESCRIBE commands must initialize a displayName if it isn't already present
if (typeof(current.displayName) !== 'string' && typeof(data.displayName) !== 'string') { throw new Error('DISPLAYNAME_REQUIRED'); }
// DESCRIBE commands must initialize a mailbox channel if it isn't already present
if (typeof(current.notifications) !== 'string' && typeof(data.displayName) !== 'string') { throw new Error('NOTIFICATIONS_REQUIRED'); }
});
var changed = false;
// then do a second pass and apply it if there were changes
Object.keys(args).forEach(function (curve) {
2019-09-18 14:27:01 +00:00
var current = Util.clone(members[curve]);
2019-09-17 09:28:22 +00:00
var data = args[curve];
Object.keys(data).forEach(function (key) {
if (current[key] === data[key]) { return; }
current[key] = data[key];
});
2019-09-18 14:27:01 +00:00
if (Sortify(current) !== Sortify(members[curve])) {
2019-09-18 13:04:08 +00:00
changed = true;
2019-09-18 14:27:01 +00:00
members[curve] = current;
2019-09-17 09:28:22 +00:00
}
2019-09-18 13:04:08 +00:00
});
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
return changed;
2019-09-17 09:28:22 +00:00
};
// XXX what about concurrent checkpoints? Let's solve for race conditions...
commands.CHECKPOINT = function (args, author, roster) {
// args: complete state
// args should be a map
2019-09-18 14:27:01 +00:00
if (!isMap(args)) { throw new Error("INVALID_CHECKPOINT_STATE"); }
2019-09-17 09:28:22 +00:00
2019-09-18 14:27:01 +00:00
if (!roster.internal.initialized) {
//console.log("INITIALIZING");
2019-09-17 09:28:22 +00:00
// either you're connecting from the beginning of the log
// or from a trusted lastKnownHash.
// Either way, initialize the roster state
roster.state = args;
2019-09-18 14:27:01 +00:00
var metadata = roster.state.metadata = roster.state.metadata || {};
metadata.topic = metadata.topic || '';
metadata.name = metadata.name || '';
metadata.avatar = metadata.avatar || '';
roster.internal.initialized = true;
2019-09-17 09:28:22 +00:00
return true;
} else if (Sortify(args) !== Sortify(roster.state)) {
// a checkpoint must reinsert the previous state
throw new Error("CHECKPOINT_DOES_NOT_MATCH_PREVIOUS_STATE");
}
// otherwise, you're iterating over the log from a previous checkpoint
// so you should know everyone's role
// owners and admins can checkpoint. members and non-members cannot
2019-09-18 14:27:01 +00:00
if (!canCheckpoint(author, roster.state.members)) { throw new Error("INSUFFICIENT_PERMISSIONS"); }
2019-09-17 09:28:22 +00:00
// set the state, and indicate that a change was made
roster.state = args;
return true;
};
2019-09-18 13:04:08 +00:00
// only admin/owner can change group metadata
commands.METADATA = function (args, author, roster) {
if (!isMap(args)) { throw new Error("INVALID_ARGS"); }
2019-09-18 14:27:01 +00:00
if (!canUpdateMetadata(author, roster.state.members)) { throw new Error("INSUFFICIENT_PERMISSIONS"); }
2019-09-18 13:04:08 +00:00
// validate inputs
Object.keys(args).forEach(function (k) {
// can't set metadata to anything other than strings
// use empty string to unset a value if you must
if (typeof(args[k]) !== 'string') { throw new Error("INVALID_ARGUMENTS"); }
});
2019-09-17 16:49:49 +00:00
2019-09-18 13:04:08 +00:00
var changed = false;
// {topic, name, avatar} are all strings...
Object.keys(args).forEach(function (k) {
// ignore things that won't cause changes
2019-09-18 14:27:01 +00:00
if (args[k] === roster.state.metadata[k]) { return; }
2019-09-17 16:49:49 +00:00
2019-09-18 13:04:08 +00:00
changed = true;
2019-09-18 14:27:01 +00:00
roster.state.metadata[k] = args[k];
2019-09-18 13:04:08 +00:00
});
return changed;
2019-09-17 16:49:49 +00:00
};
2019-09-17 09:28:22 +00:00
var handleCommand = function (content, author, roster) {
if (!(Array.isArray(content) && typeof(author) === 'string')) {
throw new Error("INVALID ARGUMENTS");
}
var command = content[0];
if (typeof(commands[command]) !== 'function') { throw new Error('INVALID_COMMAND'); }
return commands[command](content[1], author, roster);
};
var simulate = function (content, author, roster) {
2019-09-18 13:04:08 +00:00
return handleCommand(content, author, Util.clone(roster));
2019-09-17 09:28:22 +00:00
};
2019-09-11 13:41:50 +00:00
Roster.create = function (config, _cb) {
if (typeof(_cb) !== 'function') { throw new Error("EXPECTED_CALLBACK"); }
var cb = Util.once(Util.mkAsync(_cb));
if (!config.network) { return void cb("EXPECTED_NETWORK"); }
2019-09-17 09:28:22 +00:00
if (!config.channel || typeof(config.channel) !== 'string' || config.channel.length !== 32) { return void cb("EXPECTED_CHANNEL"); }
if (!config.keys || typeof(config.keys) !== 'object') { return void cb("EXPECTED_CRYPTO_KEYS"); }
if (!config.anon_rpc) { return void cb("EXPECTED_ANON_RPC"); }
2019-09-11 13:41:50 +00:00
2019-09-17 16:49:49 +00:00
2019-09-18 13:04:08 +00:00
var response = Util.response();
2019-09-17 09:28:22 +00:00
var anon_rpc = config.anon_rpc;
var keys = config.keys;
var me = keys.myCurvePublic;
var channel = config.channel;
2019-09-18 13:04:08 +00:00
var ref = {
2019-09-18 14:27:01 +00:00
state: {
members: { },
metadata: { },
},
internal: {
initialized: false,
2019-09-18 13:04:08 +00:00
},
};
2019-09-11 13:41:50 +00:00
var roster = {};
2019-09-17 09:28:22 +00:00
var events = {
change: Util.mkEvent(),
2019-09-17 16:49:49 +00:00
checkpoint: Util.mkEvent(),
2019-09-17 09:28:22 +00:00
};
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
roster.on = function (key, handler) {
if (typeof(events[key]) !== 'object') { throw new Error("unsupported event"); }
events[key].reg(handler);
2019-09-18 13:04:08 +00:00
return roster;
2019-09-17 09:28:22 +00:00
};
roster.off = function (key, handler) {
if (typeof(events[key]) !== 'object') { throw new Error("unsupported event"); }
events[key].unreg(handler);
2019-09-18 13:04:08 +00:00
return roster;
};
roster.once = function (key, handler) {
if (typeof(events[key]) !== 'object') { throw new Error("unsupported event"); }
var f = function () {
handler.apply(null, Array.prototype.slice.call(arguments));
events[key].unreg(f);
};
events[key].reg(f);
return roster;
2019-09-17 09:28:22 +00:00
};
roster.getState = function () {
2019-09-18 14:27:01 +00:00
//if (!isMap(ref.state)) { return; }
2019-09-18 13:04:08 +00:00
return Util.clone(ref.state);
2019-09-17 09:28:22 +00:00
};
2019-09-11 13:41:50 +00:00
2019-09-18 13:04:08 +00:00
var webChannel;
2019-09-17 16:49:49 +00:00
roster.stop = function () {
2019-09-18 13:04:08 +00:00
if (webChannel && typeof(webChannel.leave) === 'function') {
webChannel.leave();
} else {
console.log("FAILED TO LEAVE");
}
2019-09-17 16:49:49 +00:00
};
2019-09-11 13:41:50 +00:00
var ready = false;
2019-09-18 13:04:08 +00:00
var onReady = function (info) {
//console.log("READY");
webChannel = info;
2019-09-11 13:41:50 +00:00
ready = true;
cb(void 0, roster);
};
2019-09-17 09:28:22 +00:00
// onError (deleted or expired)
// you won't be able to connect
// onMetadataUpdate
// update owners?
// deleted while you are open
// emit an event
2019-09-11 13:41:50 +00:00
var onChannelError = function (info) {
2019-09-17 09:28:22 +00:00
if (!ready) { return void cb(info); } // XXX make sure we don't reconnect
2019-09-11 13:41:50 +00:00
console.error("CHANNEL_ERROR", info);
};
var onConnect = function (/* wc, sendMessage */) {
2019-09-17 09:28:22 +00:00
console.log("ROSTER CONNECTED");
};
2019-09-18 13:04:08 +00:00
var isReady = function () {
return Boolean(ready && me);
};
2019-09-17 16:49:49 +00:00
2019-09-17 09:28:22 +00:00
var onMessage = function (msg, user, vKey, isCp , hash, author) {
var parsed = Util.tryParse(msg);
if (!parsed) { return void console.error("could not parse"); }
var changed;
2019-09-18 13:04:08 +00:00
var error;
2019-09-17 09:28:22 +00:00
try {
changed = handleCommand(parsed, author, ref);
} catch (err) {
2019-09-18 13:04:08 +00:00
error = err;
2019-09-17 09:28:22 +00:00
}
2019-09-17 16:49:49 +00:00
var id = getMessageId(hash);
2019-09-18 13:04:08 +00:00
if (response.expected(id)) {
if (error) { return void response.handle(id, [error]); }
try {
if (!changed) {
response.handle(id, ['NO_CHANGE']);
2019-09-18 14:27:01 +00:00
console.log(msg);
2019-09-18 13:04:08 +00:00
} else {
response.handle(id, [void 0, roster.getState()]);
}
} catch (err) {
console.log('CAUGHT', err);
2019-09-17 16:49:49 +00:00
}
}
2019-09-17 09:28:22 +00:00
2019-09-18 13:04:08 +00:00
// if a checkpoint was successfully applied, emit an event
if (parsed[0] === 'CHECKPOINT' && changed) {
events.checkpoint.fire(hash);
} else if (changed) {
events.change.fire();
}
2019-09-17 09:28:22 +00:00
};
var metadata, crypto;
2019-09-18 13:04:08 +00:00
var send = function (msg, cb) {
2019-09-18 14:27:01 +00:00
if (!isReady()) { return void cb("NOT_READY"); }
2019-09-17 09:28:22 +00:00
var changed = false;
try {
// simulate the command before you send it
changed = simulate(msg, keys.myCurvePublic, ref);
} catch (err) {
return void cb(err);
}
2019-09-18 14:27:01 +00:00
if (!changed) { return void cb("NO_CHANGE"); }
2019-09-17 09:28:22 +00:00
var ciphertext = crypto.encrypt(Sortify(msg));
2019-09-17 16:49:49 +00:00
var id = getMessageId(ciphertext);
2019-09-18 13:04:08 +00:00
//console.log("Sending with id [%s]", id, msg);
//console.log();
response.expect(id, cb, 3000);
2019-09-17 09:28:22 +00:00
anon_rpc.send('WRITE_PRIVATE_MESSAGE', [
channel,
ciphertext
], function (err) {
2019-09-18 13:04:08 +00:00
if (err) { return response.handle(id, [err]); }
2019-09-17 09:28:22 +00:00
});
};
2019-09-18 13:04:08 +00:00
roster.init = function (_data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
2019-09-18 14:27:01 +00:00
if (ref.internal.initialized) { return void cb("ALREADY_INITIALIZED"); }
2019-09-18 13:04:08 +00:00
var data = Util.clone(_data);
2019-09-17 09:28:22 +00:00
data.role = 'OWNER';
2019-09-18 14:27:01 +00:00
var members = {};
members[me] = data;
send([ 'CHECKPOINT', { members: members } ], cb);
2019-09-17 09:28:22 +00:00
};
// commands
2019-09-18 13:04:08 +00:00
roster.checkpoint = function (_cb) {
var cb = Util.once(Util.mkAsync(_cb));
var state = ref.state;
2019-09-18 14:27:01 +00:00
//if (!state) { return cb("UNINITIALIZED"); }
2019-09-17 09:28:22 +00:00
send([ 'CHECKPOINT', ref.state], cb);
};
2019-09-18 13:04:08 +00:00
roster.add = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
2019-09-18 14:27:01 +00:00
//var state = ref.state;
if (!ref.internal.initialized) { return cb("UNINITIALIZED"); }
2019-09-18 13:04:08 +00:00
if (!isMap(data)) { return void cb("INVALID_ARGUMENTS"); }
// don't add members that are already present
// use DESCRIBE to amend
Object.keys(data).forEach(function (curve) {
2019-09-18 14:27:01 +00:00
if (!isValidId(curve) || isMap(ref.state.members[curve])) { return delete data[curve]; }
2019-09-18 13:04:08 +00:00
});
2019-09-17 09:28:22 +00:00
send([ 'ADD', data ], cb);
};
2019-09-18 13:04:08 +00:00
roster.remove = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var state = ref.state;
if (!state) { return cb("UNINITIALIZED"); }
if (!Array.isArray(data)) { return void cb("INVALID_ARGUMENTS"); }
var toRemove = [];
2019-09-18 14:27:01 +00:00
var current = Object.keys(state.members);
2019-09-18 13:04:08 +00:00
data.forEach(function (curve) {
// don't try to remove elements which are not in the current state
if (current.indexOf(curve) === -1) { return; }
toRemove.push(curve);
});
send([ 'RM', toRemove ], cb);
2019-09-11 13:41:50 +00:00
};
2019-09-18 13:04:08 +00:00
roster.describe = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var state = ref.state;
if (!state) { return cb("UNINITIALIZED"); }
if (!isMap(data)) { return void cb("INVALID_ARGUMENTS"); }
Object.keys(data).forEach(function (curve) {
var member = data[curve];
if (!isMap(member)) { delete data[curve]; }
// don't send fields that won't result in a change
Object.keys(member).forEach(function (k) {
2019-09-18 14:27:01 +00:00
if (member[k] === state.members[curve][k]) { delete member[k]; }
2019-09-18 13:04:08 +00:00
});
});
2019-09-17 09:28:22 +00:00
send(['DESCRIBE', data], cb);
2019-09-11 13:41:50 +00:00
};
2019-09-18 13:04:08 +00:00
roster.metadata = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
2019-09-18 14:27:01 +00:00
var metadata = ref.state.metadata;
2019-09-18 13:04:08 +00:00
if (!isMap(data)) { return void cb("INVALID_ARGUMENTS"); }
Object.keys(data).forEach(function (k) {
if (data[k] === metadata[k]) { delete data[k]; }
});
send(['METADATA', data], cb);
};
2019-09-17 09:28:22 +00:00
nThen(function (w) {
// get metadata so we know the owners and validateKey
anon_rpc.send('GET_METADATA', channel, function (err, data) {
if (err) {
w.abort();
return void console.error(err);
}
2019-09-18 13:04:08 +00:00
metadata = ref.internal.metadata = (data && data[0]) || undefined;
2019-09-17 09:28:22 +00:00
console.log("TEAM_METADATA", metadata);
});
}).nThen(function (w) {
if (!config.keys.teamEdPublic && metadata && metadata.validateKey) {
config.keys.teamEdPublic = metadata.validateKey;
}
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
try {
crypto = Crypto.Team.createEncryptor(config.keys);
} catch (err) {
w.abort();
return void cb(err);
}
}).nThen(function () {
2019-09-18 14:27:01 +00:00
var lastKnownHash = config.lastKnownHash || -1;
if (typeof(lastKnownHash) === 'string') {
console.log("Synchronizing from checkpoint");
}
2019-09-17 09:28:22 +00:00
CPNetflux.start({
// if you don't have a lastKnownHash you will need the full history
// passing -1 forces the server to send all messages, otherwise
// malicious users with the signing key could send cp| messages
// and fool new users into initializing their session incorrectly
2019-09-18 14:27:01 +00:00
lastKnownHash: lastKnownHash,
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
network: config.network,
channel: config.channel,
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
crypto: crypto,
validateKey: config.keys.teamEdPublic,
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
owners: config.owners,
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
onChannelError: onChannelError,
onReady: onReady,
onConnect: onConnect,
onConnectionChange: function () {},
onMessage: onMessage,
2019-09-11 13:41:50 +00:00
2019-09-17 09:28:22 +00:00
noChainPad: true,
});
2019-09-11 13:41:50 +00:00
});
};
return Roster;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = factory(
require("../common-util"),
require("../common-hash"),
2019-09-17 09:28:22 +00:00
require("../../bower_components/chainpad-netflux/chainpad-netflux.js"),
require("../../bower_components/json.sortify"),
require("nthen"),
require("../../bower_components/chainpad-crypto/crypto")
2019-09-11 13:41:50 +00:00
);
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define([
'/common/common-util.js',
'/common/common-hash.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
2019-09-17 09:28:22 +00:00
'/bower_compoents/json.sortify/dist/JSON.sortify.js',
'/bower_components/nthen/index.js',
'/bower_components/chainpad-crypto/crypto.js'
2019-09-11 13:41:50 +00:00
//'/bower_components/tweetnacl/nacl-fast.min.js',
2019-09-17 09:28:22 +00:00
], function (Util, Hash, CPNF, Sortify, nThen, Crypto) {
2019-09-11 13:41:50 +00:00
return factory.apply(null, [
Util,
Hash,
2019-09-17 09:28:22 +00:00
CPNF,
Sortify,
nThen,
Crypto
2019-09-11 13:41:50 +00:00
]);
});
} else {
// I'm not gonna bother supporting any other kind of instanciation
}
}());