You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.9 KiB

  1. /*jshint esversion: 6 */
  2. const Util = require("./common-util");
  3. const Core = require("./commands/core");
  4. const Admin = require("./commands/admin-rpc");
  5. const Pinning = require("./commands/pin-rpc");
  6. const Quota = require("./commands/quota");
  7. const Block = require("./commands/block");
  8. const Metadata = require("./commands/metadata");
  9. const Channel = require("./commands/channel");
  10. const Upload = require("./commands/upload");
  11. const HK = require("./hk-util");
  12. var RPC = module.exports;
  13. const UNAUTHENTICATED_CALLS = {
  14. GET_FILE_SIZE: Pinning.getFileSize,
  15. GET_MULTIPLE_FILE_SIZE: Pinning.getMultipleFileSize,
  16. GET_DELETED_PADS: Pinning.getDeletedPads,
  17. IS_CHANNEL_PINNED: Pinning.isChannelPinned, // FIXME drop this RPC
  18. IS_NEW_CHANNEL: Channel.isNewChannel,
  19. WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
  20. GET_METADATA: Metadata.getMetadata,
  21. };
  22. var isUnauthenticateMessage = function (msg) {
  23. return msg && msg.length === 2 && typeof(UNAUTHENTICATED_CALLS[msg[0]]) === 'function';
  24. };
  25. var handleUnauthenticatedMessage = function (Env, msg, respond, Server, netfluxId) {
  26. Env.Log.silly('LOG_RPC', msg[0]);
  27. var method = UNAUTHENTICATED_CALLS[msg[0]];
  28. method(Env, msg[1], function (err, value) {
  29. if (err) {
  30. Env.WARN(err, msg[1]);
  31. return void respond(err);
  32. }
  33. respond(err, [null, value, null]);
  34. }, Server, netfluxId);
  35. };
  36. const AUTHENTICATED_USER_TARGETED = {
  37. RESET: Pinning.resetUserPins,
  38. PIN: Pinning.pinChannel,
  39. UNPIN: Pinning.unpinChannel,
  40. CLEAR_OWNED_CHANNEL: Channel.clearOwnedChannel,
  41. REMOVE_OWNED_CHANNEL: Channel.removeOwnedChannel,
  42. TRIM_HISTORY: Channel.trimHistory,
  43. UPLOAD_STATUS: Upload.status,
  44. UPLOAD: Upload.upload,
  45. UPLOAD_COMPLETE: Upload.complete,
  46. UPLOAD_CANCEL: Upload.cancel,
  47. OWNED_UPLOAD_COMPLETE: Upload.complete_owned,
  48. WRITE_LOGIN_BLOCK: Block.writeLoginBlock,
  49. REMOVE_LOGIN_BLOCK: Block.removeLoginBlock,
  50. ADMIN: Admin.command,
  51. SET_METADATA: Metadata.setMetadata,
  52. };
  53. const AUTHENTICATED_USER_SCOPED = {
  54. GET_HASH: Pinning.getHash,
  55. GET_TOTAL_SIZE: Pinning.getTotalSize,
  56. UPDATE_LIMITS: Quota.getUpdatedLimit,
  57. GET_LIMIT: Pinning.getLimit,
  58. EXPIRE_SESSION: Core.expireSessionAsync,
  59. REMOVE_PINS: Pinning.removePins,
  60. TRIM_PINS: Pinning.trimPins,
  61. COOKIE: Core.haveACookie,
  62. };
  63. var isAuthenticatedCall = function (call) {
  64. if (call === 'UPLOAD') { return false; }
  65. return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function';
  66. };
  67. var handleAuthenticatedMessage = function (Env, unsafeKey, msg, respond, Server) {
  68. /* If you have gotten this far, you have signed the message with the
  69. public key which you provided.
  70. */
  71. var safeKey = Util.escapeKeyCharacters(unsafeKey);
  72. var Respond = function (e, value) {
  73. var session = Env.Sessions[safeKey];
  74. var token = session? session.tokens.slice(-1)[0]: '';
  75. var cookie = Core.makeCookie(token).join('|');
  76. respond(e ? String(e): e, [cookie].concat(typeof(value) !== 'undefined' ?value: []));
  77. };
  78. msg.shift();
  79. // discard validated cookie from message
  80. if (!msg.length) {
  81. return void Respond('INVALID_MSG');
  82. }
  83. var TYPE = msg[0];
  84. Env.Log.silly('LOG_RPC', TYPE);
  85. if (typeof(AUTHENTICATED_USER_TARGETED[TYPE]) === 'function') {
  86. return void AUTHENTICATED_USER_TARGETED[TYPE](Env, safeKey, msg[1], function (e, value) {
  87. Env.WARN(e, value);
  88. return void Respond(e, value);
  89. }, Server);
  90. }
  91. if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') {
  92. return void AUTHENTICATED_USER_SCOPED[TYPE](Env, safeKey, function (e, value) {
  93. if (e) {
  94. Env.WARN(e, safeKey);
  95. return void Respond(e);
  96. }
  97. Respond(e, value);
  98. });
  99. }
  100. return void Respond('UNSUPPORTED_RPC_CALL', msg);
  101. };
  102. var rpc = function (Env, Server, userId, data, respond) {
  103. if (!Array.isArray(data)) {
  104. Env.Log.debug('INVALID_ARG_FORMET', data);
  105. return void respond('INVALID_ARG_FORMAT');
  106. }
  107. if (!data.length) {
  108. return void respond("INSUFFICIENT_ARGS");
  109. } else if (data.length !== 1) {
  110. Env.Log.debug('UNEXPECTED_ARGUMENTS_LENGTH', data);
  111. }
  112. var msg = data[0].slice(0);
  113. if (!Array.isArray(msg)) {
  114. return void respond('INVALID_ARG_FORMAT');
  115. }
  116. if (isUnauthenticateMessage(msg)) {
  117. return handleUnauthenticatedMessage(Env, msg, respond, Server, userId);
  118. }
  119. var signature = msg.shift();
  120. var publicKey = msg.shift();
  121. // make sure a user object is initialized in the cookie jar
  122. var session;
  123. if (publicKey) {
  124. session = Core.getSession(Env.Sessions, publicKey);
  125. } else {
  126. Env.Log.debug("NO_PUBLIC_KEY_PROVIDED", publicKey);
  127. }
  128. var cookie = msg[0];
  129. if (!Core.isValidCookie(Env.Sessions, publicKey, cookie)) {
  130. // no cookie is fine if the RPC is to get a cookie
  131. if (msg[1] !== 'COOKIE') {
  132. return void respond('NO_COOKIE');
  133. }
  134. }
  135. var serialized = JSON.stringify(msg);
  136. if (!(serialized && typeof(publicKey) === 'string')) {
  137. return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
  138. }
  139. var command = msg[1];
  140. if (command === 'UPLOAD') {
  141. // UPLOAD is a special case that skips signature validation
  142. // intentional fallthrough behaviour
  143. return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
  144. }
  145. if (isAuthenticatedCall(command)) {
  146. // check the signature on the message
  147. // refuse the command if it doesn't validate
  148. return void Env.checkSignature(serialized, signature, publicKey, function (err) {
  149. if (err) {
  150. return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
  151. }
  152. HK.authenticateNetfluxSession(Env, userId, publicKey);
  153. return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
  154. });
  155. }
  156. Env.Log.warn('INVALID_RPC_CALL', command);
  157. return void respond("INVALID_RPC_CALL");
  158. };
  159. RPC.create = function (Env, cb) {
  160. var Sessions = Env.Sessions;
  161. var updateLimitDaily = function () {
  162. Quota.updateCachedLimits(Env, function (e) {
  163. if (e) {
  164. Env.WARN('limitUpdate', e);
  165. }
  166. });
  167. };
  168. Quota.applyCustomLimits(Env);
  169. updateLimitDaily();
  170. Env.intervals.dailyLimitUpdate = setInterval(updateLimitDaily, 24*3600*1000);
  171. // expire old sessions once per minute
  172. Env.intervals.sessionExpirationInterval = setInterval(function () {
  173. Core.expireSessions(Sessions);
  174. }, Core.SESSION_EXPIRATION_TIME);
  175. cb(void 0, function (Server, userId, data, respond) {
  176. try {
  177. return rpc(Env, Server, userId, data, respond);
  178. } catch (e) {
  179. console.log("Error from RPC with data " + JSON.stringify(data));
  180. console.log(e.stack);
  181. }
  182. });
  183. };