|
|
|
@ -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 isAuthenticatedCall = function (call) { |
|
|
|
if (call === 'UPLOAD') { return false; } |
|
|
|
return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function'; |
|
|
|
}; |
|
|
|
|
|
|
|
var handleAuthenticatedMessage = function (Env, map) { |
|
|
|
var msg = map.msg; |
|
|
|
var safeKey = map.safeKey; |
|
|
|
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]; |
|
|
|
|
|
|
|
@ -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"); |
|
|
|
} |
|
|
|
} else if (msg[1] !== 'UPLOAD') { |
|
|
|
Env.Log.warn('INVALID_RPC_CALL', msg[1]); |
|
|
|
return void respond("INVALID_RPC_CALL"); |
|
|
|
} |
|
|
|
var command = msg[1]; |
|
|
|
|
|
|
|
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'); |
|
|
|
if (command === 'UPLOAD') { |
|
|
|
// UPLOAD is a special case that skips signature validation
|
|
|
|
// intentional fallthrough behaviour
|
|
|
|
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server); |
|
|
|
} |
|
|
|
|
|
|
|
handleAuthenticatedMessage(Env, { |
|
|
|
msg: msg, |
|
|
|
safeKey: safeKey, |
|
|
|
Respond: Respond, |
|
|
|
Server: 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); |
|
|
|
} |
|
|
|
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY"); |
|
|
|
} |
|
|
|
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 || {}, |
|
|
|
|