mirror of https://github.com/xwiki-labs/cryptpad
continue refactoring rpc
This commit is contained in:
parent
d17e180420
commit
bde17a62a1
|
@ -2,6 +2,8 @@
|
|||
const BatchRead = require("../batch-read");
|
||||
const nThen = require("nthen");
|
||||
const getFolderSize = require("get-folder-size");
|
||||
const Util = require("../common-util");
|
||||
|
||||
var Fs = require("fs");
|
||||
|
||||
var Admin = module.exports;
|
||||
|
@ -90,9 +92,10 @@ var getDiskUsage = function (Env, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
Admin.command = function (Env, Server, publicKey, data, cb) {
|
||||
Admin.command = function (Env, safeKey, data, cb, Server) {
|
||||
var admins = Env.admins;
|
||||
if (admins.indexOf(publicKey) === -1) {
|
||||
var unsafeKey = Util.unescapeKeyCharacters(safeKey);
|
||||
if (admins.indexOf(unsafeKey) === -1) {
|
||||
return void cb("FORBIDDEN");
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ const Util = require("../common-util");
|
|||
author of the block, since we assume that the block will have been
|
||||
encrypted with xsalsa20-poly1305 which is authenticated.
|
||||
*/
|
||||
Block.validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS
|
||||
var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS
|
||||
// convert the public key to a Uint8Array and validate it
|
||||
if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); }
|
||||
|
||||
|
@ -86,13 +86,13 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS
|
|||
return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
|
||||
};
|
||||
|
||||
Block.writeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS
|
||||
Block.writeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
|
||||
//console.log(msg);
|
||||
var publicKey = msg[0];
|
||||
var signature = msg[1];
|
||||
var block = msg[2];
|
||||
|
||||
Block.validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) {
|
||||
validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) {
|
||||
if (e) { return void cb(e); }
|
||||
if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); }
|
||||
|
||||
|
@ -141,12 +141,12 @@ Block.writeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS
|
|||
information, we can just sign some constant and use that as proof.
|
||||
|
||||
*/
|
||||
Block.removeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS
|
||||
Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
|
||||
var publicKey = msg[0];
|
||||
var signature = msg[1];
|
||||
var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant
|
||||
|
||||
Block.validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) {
|
||||
validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) {
|
||||
if (e) { return void cb(e); }
|
||||
// derive the filepath
|
||||
var path = createLoginBlockPath(Env, publicKey);
|
||||
|
|
|
@ -160,7 +160,7 @@ Channel.isNewChannel = function (Env, channel, cb) {
|
|||
|
||||
Otherwise behaves the same as sending to a channel
|
||||
*/
|
||||
Channel.writePrivateMessage = function (Env, args, Server, cb) {
|
||||
Channel.writePrivateMessage = function (Env, args, cb, Server) { // XXX odd signature
|
||||
var channelId = args[0];
|
||||
var msg = args[1];
|
||||
|
||||
|
|
|
@ -184,5 +184,7 @@ Core.isPendingOwner = function (metadata, unsafeKey) {
|
|||
return metadata.pending_owners.indexOf(unsafeKey) !== -1;
|
||||
};
|
||||
|
||||
|
||||
Core.haveACookie = function (Env, safeKey, cb) {
|
||||
cb();
|
||||
};
|
||||
|
||||
|
|
|
@ -8,10 +8,12 @@ const Core = require("./core");
|
|||
const Util = require("../common-util");
|
||||
|
||||
const batchMetadata = BatchRead("GET_METADATA");
|
||||
Data.getMetadata = function (Env, channel, cb) {
|
||||
Data.getMetadata = function (Env, channel, cb/* , Server */) {
|
||||
if (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); }
|
||||
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); }
|
||||
|
||||
// XXX get metadata from the server cache if it is available
|
||||
// Server isn't always passed, though...
|
||||
batchMetadata(channel, cb, function (done) {
|
||||
var ref = {};
|
||||
var lineHandler = Meta.createLineHandler(ref, Env.Log.error);
|
||||
|
|
|
@ -454,10 +454,10 @@ Pinning.loadChannelPins = function (Env) {
|
|||
Pinning.isChannelPinned = function (Env, channel, cb) {
|
||||
Env.evPinnedPadsReady.reg(() => {
|
||||
if (Env.pinnedPads[channel] && Object.keys(Env.pinnedPads[channel]).length) {
|
||||
cb(true);
|
||||
cb(void 0, true);
|
||||
} else {
|
||||
delete Env.pinnedPads[channel];
|
||||
cb(false);
|
||||
delete Env.pinnedPads[channel]; // XXX WAT
|
||||
cb(void 0, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
223
lib/rpc.js
223
lib/rpc.js
|
@ -18,98 +18,30 @@ var RPC = module.exports;
|
|||
const Store = require("../storage/file");
|
||||
const BlobStore = require("../storage/blob");
|
||||
|
||||
const UNAUTHENTICATED_CALLS = [
|
||||
'GET_FILE_SIZE',
|
||||
'GET_METADATA',
|
||||
'GET_MULTIPLE_FILE_SIZE',
|
||||
'IS_CHANNEL_PINNED',
|
||||
'IS_NEW_CHANNEL',
|
||||
'GET_DELETED_PADS',
|
||||
'WRITE_PRIVATE_MESSAGE',
|
||||
];
|
||||
|
||||
var isUnauthenticatedCall = function (call) {
|
||||
return UNAUTHENTICATED_CALLS.indexOf(call) !== -1;
|
||||
};
|
||||
|
||||
const AUTHENTICATED_CALLS = [
|
||||
'COOKIE',
|
||||
'RESET',
|
||||
'PIN',
|
||||
'UNPIN',
|
||||
'GET_HASH',
|
||||
'GET_TOTAL_SIZE',
|
||||
'UPDATE_LIMITS',
|
||||
'GET_LIMIT',
|
||||
'UPLOAD_STATUS',
|
||||
'UPLOAD_COMPLETE',
|
||||
'OWNED_UPLOAD_COMPLETE',
|
||||
'UPLOAD_CANCEL',
|
||||
'EXPIRE_SESSION',
|
||||
'TRIM_OWNED_CHANNEL_HISTORY',
|
||||
'CLEAR_OWNED_CHANNEL',
|
||||
'REMOVE_OWNED_CHANNEL',
|
||||
'REMOVE_PINS',
|
||||
'TRIM_PINS',
|
||||
'WRITE_LOGIN_BLOCK',
|
||||
'REMOVE_LOGIN_BLOCK',
|
||||
'ADMIN',
|
||||
'SET_METADATA'
|
||||
];
|
||||
|
||||
var isAuthenticatedCall = function (call) {
|
||||
return AUTHENTICATED_CALLS.indexOf(call) !== -1;
|
||||
const UNAUTHENTICATED_CALLS = {
|
||||
GET_FILE_SIZE: Pinning.getFileSize, // XXX TEST
|
||||
GET_MULTIPLE_FILE_SIZE: Pinning.getMultipleFileSize,
|
||||
GET_DELETED_PADS: Pinning.getDeletedPads,
|
||||
IS_CHANNEL_PINNED: Pinning.isChannelPinned,
|
||||
IS_NEW_CHANNEL: Channel.isNewChannel,
|
||||
WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
|
||||
};
|
||||
|
||||
var isUnauthenticateMessage = function (msg) {
|
||||
return msg && msg.length === 2 && isUnauthenticatedCall(msg[0]);
|
||||
return msg && msg.length === 2 && typeof(UNAUTHENTICATED_CALLS[msg[0]]) === 'function';
|
||||
};
|
||||
|
||||
var handleUnauthenticatedMessage = function (Env, msg, respond, Server) {
|
||||
Env.Log.silly('LOG_RPC', msg[0]);
|
||||
switch (msg[0]) {
|
||||
case 'GET_FILE_SIZE':
|
||||
return void Pinning.getFileSize(Env, msg[1], function (e, size) {
|
||||
Env.WARN(e, msg[1]);
|
||||
respond(e, [null, size, null]);
|
||||
});
|
||||
case 'GET_METADATA':
|
||||
return void Metadata.getMetadata(Env, msg[1], function (e, data) {
|
||||
Env.WARN(e, msg[1]);
|
||||
respond(e, [null, data, null]);
|
||||
});
|
||||
case 'GET_MULTIPLE_FILE_SIZE': // XXX not actually used on the client?
|
||||
return void Pinning.getMultipleFileSize(Env, msg[1], function (e, dict) {
|
||||
if (e) {
|
||||
Env.WARN(e, dict);
|
||||
return respond(e);
|
||||
}
|
||||
respond(e, [null, dict, null]);
|
||||
});
|
||||
case 'GET_DELETED_PADS':
|
||||
return void Pinning.getDeletedPads(Env, msg[1], function (e, list) {
|
||||
if (e) {
|
||||
Env.WARN(e, msg[1]);
|
||||
return respond(e);
|
||||
}
|
||||
respond(e, [null, list, null]);
|
||||
});
|
||||
case 'IS_CHANNEL_PINNED':
|
||||
return void Pinning.isChannelPinned(Env, msg[1], function (isPinned) {
|
||||
respond(null, [null, isPinned, null]);
|
||||
});
|
||||
case 'IS_NEW_CHANNEL':
|
||||
return void Channel.isNewChannel(Env, msg[1], function (e, isNew) {
|
||||
respond(e, [null, isNew, null]);
|
||||
});
|
||||
case 'WRITE_PRIVATE_MESSAGE':
|
||||
return void Channel.writePrivateMessage(Env, msg[1], Server, function (e, output) {
|
||||
respond(e, output);
|
||||
});
|
||||
default:
|
||||
Env.Log.warn("UNSUPPORTED_RPC_CALL", msg);
|
||||
return respond('UNSUPPORTED_RPC_CALL', msg);
|
||||
}
|
||||
|
||||
var method = UNAUTHENTICATED_CALLS[msg[0]];
|
||||
method(Env, msg[1], function (err, value) {
|
||||
if (err) {
|
||||
Env.WARN(err, msg[1]);
|
||||
return void respond(err);
|
||||
}
|
||||
respond(err, [null, value, null]);
|
||||
}, Server);
|
||||
};
|
||||
|
||||
const AUTHENTICATED_USER_TARGETED = {
|
||||
|
@ -124,6 +56,9 @@ const AUTHENTICATED_USER_TARGETED = {
|
|||
UPLOAD_COMPLETE: Upload.complete,
|
||||
UPLOAD_CANCEL: Upload.cancel,
|
||||
OWNED_UPLOAD_COMPLETE: Upload.complete_owned,
|
||||
WRITE_LOGIN_BLOCK: Block.writeLoginBlock,
|
||||
REMOVE_LOGIN_BLOCK: Block.removeLoginBlock,
|
||||
ADMIN: Admin.command,
|
||||
};
|
||||
|
||||
const AUTHENTICATED_USER_SCOPED = {
|
||||
|
@ -135,13 +70,33 @@ const AUTHENTICATED_USER_SCOPED = {
|
|||
REMOVE_PINS: Pinning.removePins,
|
||||
TRIM_PINS: Pinning.trimPins,
|
||||
SET_METADATA: Metadata.setMetadata,
|
||||
COOKIE: Core.haveACookie,
|
||||
};
|
||||
|
||||
var handleAuthenticatedMessage = function (Env, map) {
|
||||
var msg = map.msg;
|
||||
var safeKey = map.safeKey;
|
||||
var Respond = map.Respond;
|
||||
var Server = map.Server;
|
||||
var isAuthenticatedCall = function (call) {
|
||||
if (call === 'UPLOAD') { return false; }
|
||||
return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function';
|
||||
};
|
||||
|
||||
var handleAuthenticatedMessage = function (Env, unsafeKey, msg, respond, Server) {
|
||||
/* If you have gotten this far, you have signed the message with the
|
||||
public key which you provided.
|
||||
*/
|
||||
|
||||
var safeKey = Util.escapeKeyCharacters(unsafeKey);
|
||||
|
||||
var Respond = function (e, value) {
|
||||
var session = Env.Sessions[safeKey];
|
||||
var token = session? session.tokens.slice(-1)[0]: '';
|
||||
var cookie = Core.makeCookie(token).join('|');
|
||||
respond(e ? String(e): e, [cookie].concat(typeof(value) !== 'undefined' ?value: []));
|
||||
};
|
||||
|
||||
msg.shift();
|
||||
// discard validated cookie from message
|
||||
if (!msg.length) {
|
||||
return void Respond('INVALID_MSG');
|
||||
}
|
||||
|
||||
var TYPE = msg[0];
|
||||
|
||||
|
@ -151,7 +106,7 @@ var handleAuthenticatedMessage = function (Env, map) {
|
|||
return void AUTHENTICATED_USER_TARGETED[TYPE](Env, safeKey, msg[1], function (e, value) {
|
||||
Env.WARN(e, value);
|
||||
return void Respond(e, value);
|
||||
});
|
||||
}, Server);
|
||||
}
|
||||
|
||||
if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') {
|
||||
|
@ -164,35 +119,7 @@ var handleAuthenticatedMessage = function (Env, map) {
|
|||
});
|
||||
}
|
||||
|
||||
switch (msg[0]) {
|
||||
case 'COOKIE': return void Respond(void 0);
|
||||
case 'WRITE_LOGIN_BLOCK':
|
||||
return void Block.writeLoginBlock(Env, msg[1], function (e) { // XXX SPECIAL
|
||||
if (e) {
|
||||
Env.WARN(e, 'WRITE_LOGIN_BLOCK');
|
||||
return void Respond(e);
|
||||
}
|
||||
Respond(e);
|
||||
});
|
||||
case 'REMOVE_LOGIN_BLOCK':
|
||||
return void Block.removeLoginBlock(Env, msg[1], function (e) { // XXX SPECIAL
|
||||
if (e) {
|
||||
Env.WARN(e, 'REMOVE_LOGIN_BLOCK');
|
||||
return void Respond(e);
|
||||
}
|
||||
Respond(e);
|
||||
});
|
||||
case 'ADMIN':
|
||||
return void Admin.command(Env, Server, safeKey, msg[1], function (e, result) { // XXX SPECIAL
|
||||
if (e) {
|
||||
Env.WARN(e, result);
|
||||
return void Respond(e);
|
||||
}
|
||||
Respond(void 0, result);
|
||||
});
|
||||
default:
|
||||
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
||||
}
|
||||
return void Respond('UNSUPPORTED_RPC_CALL', msg);
|
||||
};
|
||||
|
||||
var rpc = function (Env, Server, data, respond) {
|
||||
|
@ -241,45 +168,23 @@ var rpc = function (Env, Server, data, respond) {
|
|||
return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
|
||||
}
|
||||
|
||||
if (isAuthenticatedCall(msg[1])) {
|
||||
if (Core.checkSignature(Env, serialized, signature, publicKey) !== true) {
|
||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||
var command = msg[1];
|
||||
|
||||
if (command === 'UPLOAD') {
|
||||
// UPLOAD is a special case that skips signature validation
|
||||
// intentional fallthrough behaviour
|
||||
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
||||
}
|
||||
if (isAuthenticatedCall(command)) {
|
||||
// check the signature on the message
|
||||
// refuse the command if it doesn't validate
|
||||
if (Core.checkSignature(Env, serialized, signature, publicKey) === true) {
|
||||
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
||||
}
|
||||
} else if (msg[1] !== 'UPLOAD') {
|
||||
Env.Log.warn('INVALID_RPC_CALL', msg[1]);
|
||||
return void respond("INVALID_RPC_CALL");
|
||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||
}
|
||||
|
||||
var safeKey = Util.escapeKeyCharacters(publicKey);
|
||||
/* If you have gotten this far, you have signed the message with the
|
||||
public key which you provided.
|
||||
|
||||
We can safely modify the state for that key
|
||||
|
||||
OR it's an unauthenticated call, which must not modify the state
|
||||
for that key in a meaningful way.
|
||||
*/
|
||||
|
||||
// discard validated cookie from message
|
||||
msg.shift();
|
||||
|
||||
var Respond = function (e, msg) {
|
||||
var session = Env.Sessions[safeKey];
|
||||
var token = session? session.tokens.slice(-1)[0]: '';
|
||||
var cookie = Core.makeCookie(token).join('|');
|
||||
respond(e ? String(e): e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: []));
|
||||
};
|
||||
|
||||
if (typeof(msg) !== 'object' || !msg.length) {
|
||||
return void Respond('INVALID_MSG');
|
||||
}
|
||||
|
||||
handleAuthenticatedMessage(Env, {
|
||||
msg: msg,
|
||||
safeKey: safeKey,
|
||||
Respond: Respond,
|
||||
Server: Server,
|
||||
});
|
||||
Env.Log.warn('INVALID_RPC_CALL', command);
|
||||
return void respond("INVALID_RPC_CALL");
|
||||
};
|
||||
|
||||
RPC.create = function (config, cb) {
|
||||
|
@ -302,6 +207,10 @@ RPC.create = function (config, cb) {
|
|||
}
|
||||
};
|
||||
|
||||
if (typeof(config.domain) !== 'undefined') {
|
||||
throw new Error('fuck');
|
||||
}
|
||||
|
||||
var Env = {
|
||||
historyKeeper: config.historyKeeper,
|
||||
intervals: config.intervals || {},
|
||||
|
|
Loading…
Reference in New Issue