Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

pull/1/head
yflory 5 years ago
commit 2411932229

@ -400,6 +400,11 @@ module.exports.create = function (cfg) {
* writes messages to the store * writes messages to the store
*/ */
const onChannelMessage = function (ctx, channel, msgStruct) { const onChannelMessage = function (ctx, channel, msgStruct) {
// TODO our usage of 'channel' here looks prone to errors
// we only use it for its 'id', but it can contain other stuff
// also, we're using this RPC from both the RPC and Netflux-server
// we should probably just change this to expect a channel id directly
// don't store messages if the channel id indicates that it's an ephemeral message // don't store messages if the channel id indicates that it's an ephemeral message
if (!channel.id || channel.id.length === EPHEMERAL_CHANNEL_LENGTH) { return; } if (!channel.id || channel.id.length === EPHEMERAL_CHANNEL_LENGTH) { return; }
@ -986,7 +991,6 @@ module.exports.create = function (cfg) {
onChannelCleared(ctx, msg[4]); onChannelCleared(ctx, msg[4]);
} }
// FIXME METADATA CHANGE
if (msg[3] === 'SET_METADATA') { // or whatever we call the RPC???? if (msg[3] === 'SET_METADATA') { // or whatever we call the RPC????
// make sure we update our cache of metadata // make sure we update our cache of metadata
// or at least invalidate it and force other mechanisms to recompute its state // or at least invalidate it and force other mechanisms to recompute its state
@ -994,6 +998,12 @@ module.exports.create = function (cfg) {
onChannelMetadataChanged(ctx, msg[4].channel, output[1]); onChannelMetadataChanged(ctx, msg[4].channel, output[1]);
} }
// unauthenticated RPC calls have a different message format
if (msg[0] === "WRITE_PRIVATE_MESSAGE" && output) {
historyKeeperBroadcast(ctx, output.channel, output.message);
}
// finally, send a response to the client that sent the RPC
sendMsg(ctx, user, [0, HISTORY_KEEPER_ID, 'MSG', user.id, JSON.stringify([parsed[0]].concat(output))]); sendMsg(ctx, user, [0, HISTORY_KEEPER_ID, 'MSG', user.id, JSON.stringify([parsed[0]].concat(output))]);
}); });
} catch (e) { } catch (e) {

@ -0,0 +1,119 @@
var Netflux = require("netflux-websocket");
var WebSocket = require("ws"); // jshint ignore:line
var nThen = require("nthen");
var Util = require("../../www/common/common-util");
var Rpc = require("../../www/common/rpc");
var Nacl = require("tweetnacl");
var makeKeys = function () {
var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
return {
secret: Nacl.util.encodeBase64(keys.secretKey),
public: Nacl.util.encodeBase64(keys.publicKey),
};
};
var Client = module.exports;
var createNetwork = Client.createNetwork = function (url, cb) {
var CB = Util.once(cb);
var info = {};
Netflux.connect(url, function (url) {
info.websocket = new WebSocket(url)
.on('error', function (err) {
console.log(err);
})
.on('close', function (err) {
console.log("close");
console.log(err);
});
return info.websocket;
}).then(function (network) {
info.network = network;
CB(void 0, info);
}, function (err) {
CB(err);
});
};
var die = function (client) {
var disconnect = Util.find(client, ['config', 'network', 'disconnect']);
if (typeof(disconnect) === 'function') {
disconnect();
} else {
console.error("disconnect was not a function");
}
var close = Util.find(client, ['config', 'websocket', 'close']);
if (typeof(close) === 'function') {
client.config.websocket.close();
} else {
console.error("close was not a function");
}
};
Client.create = function (config, cb) {
if (typeof(config) === 'function') {
cb = config;
config = {};
}
var client = {
config: config,
};
var CB = Util.once(function (err, arg) {
if (err) { die(client); }
cb(err, arg);
});
client.shutdown = function () {
die(client);
};
nThen(function (w) {
if (config.network) { return; }
// connect to the network...
createNetwork('ws://localhost:3000/cryptpad_websocket', w(function (err, info) {
//console.log(_network);
config.network = info.network;
config.websocket = info.websocket;
}));
}).nThen(function (w) {
// make sure the network has a historyKeeper id on it
// we're responsible for adding it
if (config.network.historyKeeper) { return; }
var channel = Util.uint8ArrayToHex(Nacl.randomBytes(16));
config.network.join(channel).then(w(function (wc) {
wc.members.some(function (member) {
if (member.length !== 16) { return; }
config.network.historyKeeper = member;
return true;
});
wc.leave();
}), function (err) {
w.abort();
CB(err);
});
}).nThen(function (w) {
// connect to the anonRpc
Rpc.createAnonymous(config.network, w(function (err, rpc) {
if (err) {
return void CB('ANON_RPC_CONNECT_ERR');
}
client.anonRpc = rpc;
}));
var keys = makeKeys();
Rpc.create(config.network, keys.secret, keys.public, w(function (err, rpc) {
if (err) {
return void CB('RPC_CONNECT_ERR');
}
client.rpc = rpc;
}));
}).nThen(function () {
CB(void 0, client);
});
};

7
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "cryptpad", "name": "cryptpad",
"version": "3.0.0", "version": "3.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -800,6 +800,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"netflux-websocket": {
"version": "0.1.20",
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.20.tgz",
"integrity": "sha512-svFkw4ol4gmkcXKnx5kF/8tR9mmtTCDzUlLy4mSlcNl/4iWlbDmgwp/+aJ3nqtv8fg12m+DAFGX2+fbC0//dcg=="
},
"nthen": { "nthen": {
"version": "0.1.8", "version": "0.1.8",
"resolved": "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz", "resolved": "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz",

@ -11,6 +11,8 @@
"chainpad-server": "~3.0.2", "chainpad-server": "~3.0.2",
"express": "~4.16.0", "express": "~4.16.0",
"fs-extra": "^7.0.0", "fs-extra": "^7.0.0",
"get-folder-size": "^2.0.1",
"netflux-websocket": "^0.1.20",
"nthen": "0.1.8", "nthen": "0.1.8",
"pull-stream": "^3.6.1", "pull-stream": "^3.6.1",
"replify": "^1.2.0", "replify": "^1.2.0",
@ -18,8 +20,7 @@
"sortify": "^1.0.4", "sortify": "^1.0.4",
"stream-to-pull-stream": "^1.7.2", "stream-to-pull-stream": "^1.7.2",
"tweetnacl": "~0.12.2", "tweetnacl": "~0.12.2",
"ws": "^3.3.1", "ws": "^3.3.1"
"get-folder-size": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
"flow-bin": "^0.59.0", "flow-bin": "^0.59.0",
@ -38,6 +39,7 @@
"lint:less": "./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/", "lint:less": "./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/",
"flow": "./node_modules/.bin/flow", "flow": "./node_modules/.bin/flow",
"test": "node scripts/TestSelenium.js", "test": "node scripts/TestSelenium.js",
"test-rpc": "cd scripts && node test-rpc",
"template": "cd customize.dist/src && for page in ../index.html ../privacy.html ../terms.html ../about.html ../contact.html ../what-is-cryptpad.html ../features.html ../../www/login/index.html ../../www/register/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;" "template": "cd customize.dist/src && for page in ../index.html ../privacy.html ../terms.html ../about.html ../contact.html ../what-is-cryptpad.html ../features.html ../../www/login/index.html ../../www/register/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;"
} }
} }

@ -1555,6 +1555,57 @@ var isNewChannel = function (Env, channel, cb) {
}); });
}; };
/* writePrivateMessage
allows users to anonymously send a message to the channel
prevents their netflux-id from being stored in history
and from being broadcast to anyone that might currently be in the channel
Otherwise behaves the same as sending to a channel
*/
var writePrivateMessage = function (Env, args, nfwssCtx, cb) {
var channelId = args[0];
var msg = args[1];
// don't bother handling empty messages
if (!msg) { return void cb("INVALID_MESSAGE"); }
// don't support anything except regular channels
if (!isValidId(channelId) || channelId.length !== 32) {
return void cb("INVALID_CHAN");
}
// We expect a modern netflux-websocket-server instance
// if this API isn't here everything will fall apart anyway
if (!(nfwssCtx && nfwssCtx.historyKeeper && typeof(nfwssCtx.historyKeeper.onChannelMessage) === 'function')) {
return void cb("NOT_IMPLEMENTED");
}
// historyKeeper expects something with an 'id' attribute
// it will fail unless you provide it, but it doesn't need anything else
var channelStruct = {
id: channelId,
};
// construct a message to store and broadcast
var fullMessage = [
0, // idk
null, // normally the netflux id, null isn't rejected, and it distinguishes messages written in this way
"MSG", // indicate that this is a MSG
channelId, // channel id
msg // the actual message content. Generally a string
];
// store the message and do everything else that is typically done when going through historyKeeper
nfwssCtx.historyKeeper.onChannelMessage(nfwssCtx, channelStruct, fullMessage);
// call back with the message and the target channel.
// historyKeeper will take care of broadcasting it if anyone is in the channel
cb(void 0, {
channel: channelId,
message: fullMessage
});
};
var getDiskUsage = function (Env, cb) { var getDiskUsage = function (Env, cb) {
var data = {}; var data = {};
nThen(function (waitFor) { nThen(function (waitFor) {
@ -1654,6 +1705,7 @@ var isUnauthenticatedCall = function (call) {
'IS_NEW_CHANNEL', 'IS_NEW_CHANNEL',
'GET_HISTORY_OFFSET', 'GET_HISTORY_OFFSET',
'GET_DELETED_PADS', 'GET_DELETED_PADS',
'WRITE_PRIVATE_MESSAGE',
].indexOf(call) !== -1; ].indexOf(call) !== -1;
}; };
@ -1821,6 +1873,10 @@ RPC.create = function (
return void isNewChannel(Env, msg[1], function (e, isNew) { return void isNewChannel(Env, msg[1], function (e, isNew) {
respond(e, [null, isNew, null]); respond(e, [null, isNew, null]);
}); });
case 'WRITE_PRIVATE_MESSAGE':
return void writePrivateMessage(Env, msg[1], nfwssCtx, function (e, output) {
respond(e, output);
});
default: default:
Log.warn("UNSUPPORTED_RPC_CALL", msg); Log.warn("UNSUPPORTED_RPC_CALL", msg);
return respond('UNSUPPORTED_RPC_CALL', msg); return respond('UNSUPPORTED_RPC_CALL', msg);

@ -0,0 +1,43 @@
/* globals process */
var Client = require("../lib/client/");
var Mailbox = require("../www/bower_components/chainpad-crypto").Mailbox;
var Nacl = require("tweetnacl");
var makeKeys = function () {
var pair = Nacl.box.keyPair();
return {
curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
curvePublic: Nacl.util.encodeBase64(pair.publicKey),
};
};
Client.create(function (err, client) {
if (err) {
console.error(err);
process.exit(1);
}
var channel = "d34ebe83931382fcad9fe2e2d0e2cb5f"; // channel
var recipient = "e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmw="; // curvePublic
var keys = makeKeys();
var cryptor = Mailbox.createEncryptor(keys);
var message = cryptor.encrypt(JSON.stringify({
type: "CHEESE",
author: keys.curvePublic,
content: {
text: "CAMEMBERT",
}
}), recipient);
client.anonRpc.send('WRITE_PRIVATE_MESSAGE', [channel, message], function (err, response) {
if (err) {
return void console.error(err);
}
response = response;
// shutdown doesn't work, so we need to do this instead
client.shutdown();
});
});

@ -1,7 +1,5 @@
(function (window) { (function (window) {
define([], function () { var Util = {};
var Util = window.CryptPad_Util = {};
Util.mkAsync = function (f) { Util.mkAsync = function (f) {
return function () { return function () {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
@ -349,6 +347,14 @@ define([], function () {
return false; return false;
}; };
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = Util;
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define([], function () {
window.CryptPad_Util = Util;
return Util; return Util;
}); });
}(self)); } else {
window.CryptPad_Util = Util;
}
}(typeof(self) !== 'undefined'? self: this));

@ -1,9 +1,5 @@
define([ (function () {
'/common/common-util.js', var factory = function (Util, Nacl) {
'/bower_components/tweetnacl/nacl-fast.min.js',
], function (Util) {
var Nacl = window.nacl;
var uid = Util.uid; var uid = Util.uid;
var signMsg = function (data, signKey) { var signMsg = function (data, signKey) {
var buffer = Nacl.util.decodeUTF8(JSON.stringify(data)); var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
@ -337,4 +333,18 @@ types of messages:
}; };
return { create: create, createAnonymous: createAnonymous }; return { create: create, createAnonymous: createAnonymous };
}); };
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = factory(require("./common-util"), require("tweetnacl"));
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define([
'/common/common-util.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function (Util) {
return factory(Util, window.Nacl);
});
} else {
// I'm not gonna bother supporting any other kind of instanciation
}
}());

Loading…
Cancel
Save