continue refactoring rpc

This commit is contained in:
ansuz
2020-02-05 17:31:44 -05:00
parent d17e180420
commit bde17a62a1
7 changed files with 86 additions and 170 deletions

View File

@@ -2,6 +2,8 @@
const BatchRead = require("../batch-read"); const BatchRead = require("../batch-read");
const nThen = require("nthen"); const nThen = require("nthen");
const getFolderSize = require("get-folder-size"); const getFolderSize = require("get-folder-size");
const Util = require("../common-util");
var Fs = require("fs"); var Fs = require("fs");
var Admin = module.exports; 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; var admins = Env.admins;
if (admins.indexOf(publicKey) === -1) { var unsafeKey = Util.unescapeKeyCharacters(safeKey);
if (admins.indexOf(unsafeKey) === -1) {
return void cb("FORBIDDEN"); return void cb("FORBIDDEN");
} }

View File

@@ -31,7 +31,7 @@ const Util = require("../common-util");
author of the block, since we assume that the block will have been author of the block, since we assume that the block will have been
encrypted with xsalsa20-poly1305 which is authenticated. 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 // convert the public key to a Uint8Array and validate it
if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); } 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); 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); //console.log(msg);
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = msg[2]; 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 (e) { return void cb(e); }
if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); } 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. 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 publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant 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); } if (e) { return void cb(e); }
// derive the filepath // derive the filepath
var path = createLoginBlockPath(Env, publicKey); var path = createLoginBlockPath(Env, publicKey);

View File

@@ -160,7 +160,7 @@ Channel.isNewChannel = function (Env, channel, cb) {
Otherwise behaves the same as sending to a channel 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 channelId = args[0];
var msg = args[1]; var msg = args[1];

View File

@@ -184,5 +184,7 @@ Core.isPendingOwner = function (metadata, unsafeKey) {
return metadata.pending_owners.indexOf(unsafeKey) !== -1; return metadata.pending_owners.indexOf(unsafeKey) !== -1;
}; };
Core.haveACookie = function (Env, safeKey, cb) {
cb();
};

View File

@@ -8,10 +8,12 @@ const Core = require("./core");
const Util = require("../common-util"); const Util = require("../common-util");
const batchMetadata = BatchRead("GET_METADATA"); 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 (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); } 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) { batchMetadata(channel, cb, function (done) {
var ref = {}; var ref = {};
var lineHandler = Meta.createLineHandler(ref, Env.Log.error); var lineHandler = Meta.createLineHandler(ref, Env.Log.error);

View File

@@ -454,10 +454,10 @@ Pinning.loadChannelPins = function (Env) {
Pinning.isChannelPinned = function (Env, channel, cb) { Pinning.isChannelPinned = function (Env, channel, cb) {
Env.evPinnedPadsReady.reg(() => { Env.evPinnedPadsReady.reg(() => {
if (Env.pinnedPads[channel] && Object.keys(Env.pinnedPads[channel]).length) { if (Env.pinnedPads[channel] && Object.keys(Env.pinnedPads[channel]).length) {
cb(true); cb(void 0, true);
} else { } else {
delete Env.pinnedPads[channel]; delete Env.pinnedPads[channel]; // XXX WAT
cb(false); cb(void 0, false);
} }
}); });
}; };

View File

@@ -18,98 +18,30 @@ var RPC = module.exports;
const Store = require("../storage/file"); const Store = require("../storage/file");
const BlobStore = require("../storage/blob"); const BlobStore = require("../storage/blob");
const UNAUTHENTICATED_CALLS = [ const UNAUTHENTICATED_CALLS = {
'GET_FILE_SIZE', GET_FILE_SIZE: Pinning.getFileSize, // XXX TEST
'GET_METADATA', GET_MULTIPLE_FILE_SIZE: Pinning.getMultipleFileSize,
'GET_MULTIPLE_FILE_SIZE', GET_DELETED_PADS: Pinning.getDeletedPads,
'IS_CHANNEL_PINNED', IS_CHANNEL_PINNED: Pinning.isChannelPinned,
'IS_NEW_CHANNEL', IS_NEW_CHANNEL: Channel.isNewChannel,
'GET_DELETED_PADS', WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
'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;
}; };
var isUnauthenticateMessage = function (msg) { 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) { var handleUnauthenticatedMessage = function (Env, msg, respond, Server) {
Env.Log.silly('LOG_RPC', msg[0]); Env.Log.silly('LOG_RPC', msg[0]);
switch (msg[0]) {
case 'GET_FILE_SIZE': var method = UNAUTHENTICATED_CALLS[msg[0]];
return void Pinning.getFileSize(Env, msg[1], function (e, size) { method(Env, msg[1], function (err, value) {
Env.WARN(e, msg[1]); if (err) {
respond(e, [null, size, null]); Env.WARN(err, msg[1]);
}); return void respond(err);
case 'GET_METADATA': }
return void Metadata.getMetadata(Env, msg[1], function (e, data) { respond(err, [null, value, null]);
Env.WARN(e, msg[1]); }, Server);
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);
}
}; };
const AUTHENTICATED_USER_TARGETED = { const AUTHENTICATED_USER_TARGETED = {
@@ -124,6 +56,9 @@ const AUTHENTICATED_USER_TARGETED = {
UPLOAD_COMPLETE: Upload.complete, UPLOAD_COMPLETE: Upload.complete,
UPLOAD_CANCEL: Upload.cancel, UPLOAD_CANCEL: Upload.cancel,
OWNED_UPLOAD_COMPLETE: Upload.complete_owned, OWNED_UPLOAD_COMPLETE: Upload.complete_owned,
WRITE_LOGIN_BLOCK: Block.writeLoginBlock,
REMOVE_LOGIN_BLOCK: Block.removeLoginBlock,
ADMIN: Admin.command,
}; };
const AUTHENTICATED_USER_SCOPED = { const AUTHENTICATED_USER_SCOPED = {
@@ -135,13 +70,33 @@ const AUTHENTICATED_USER_SCOPED = {
REMOVE_PINS: Pinning.removePins, REMOVE_PINS: Pinning.removePins,
TRIM_PINS: Pinning.trimPins, TRIM_PINS: Pinning.trimPins,
SET_METADATA: Metadata.setMetadata, SET_METADATA: Metadata.setMetadata,
COOKIE: Core.haveACookie,
}; };
var handleAuthenticatedMessage = function (Env, map) { var isAuthenticatedCall = function (call) {
var msg = map.msg; if (call === 'UPLOAD') { return false; }
var safeKey = map.safeKey; return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function';
var Respond = map.Respond; };
var Server = map.Server;
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]; 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) { return void AUTHENTICATED_USER_TARGETED[TYPE](Env, safeKey, msg[1], function (e, value) {
Env.WARN(e, value); Env.WARN(e, value);
return void Respond(e, value); return void Respond(e, value);
}); }, Server);
} }
if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') { if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') {
@@ -164,35 +119,7 @@ var handleAuthenticatedMessage = function (Env, map) {
}); });
} }
switch (msg[0]) { return void Respond('UNSUPPORTED_RPC_CALL', msg);
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);
}
}; };
var rpc = function (Env, Server, data, respond) { 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'); return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
} }
if (isAuthenticatedCall(msg[1])) { var command = msg[1];
if (Core.checkSignature(Env, serialized, signature, publicKey) !== true) {
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY"); 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') { return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
Env.Log.warn('INVALID_RPC_CALL', msg[1]);
return void respond("INVALID_RPC_CALL");
} }
Env.Log.warn('INVALID_RPC_CALL', command);
var safeKey = Util.escapeKeyCharacters(publicKey); return void respond("INVALID_RPC_CALL");
/* 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,
});
}; };
RPC.create = function (config, cb) { 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 = { var Env = {
historyKeeper: config.historyKeeper, historyKeeper: config.historyKeeper,
intervals: config.intervals || {}, intervals: config.intervals || {},