1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-30 08:09:15 +01:00
metamask-extension/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js
Erik Marks 05fae3fa1e
Add RPC method handler hook selection (#12664)
Adds a property, `hookNames`, to each RPC method handler export in `app/scripts/lib/rpc-method-middleware` and a function, `selectHooks`, to select from them.

`createMethodMiddleware` receives a giant `opts` object that includes a bunch of different methods from `MetaMaskController` and its subcontrollers. Each method implementation only requires a subset of these methods to do its work. Because they need some kind of name, we call these methods "hooks". With this change, whenever an RPC method is called, `selectHooks` will be called to ensure that each method only receives the hooks that it needs in order to do its job.

This implementation is based on [work in `snaps-skunkworks`](https://github.com/MetaMask/snaps-skunkworks/blob/a3e1248/packages/rpc-methods/src/utils.ts#L17-L34) that will be merged in the near future.
2021-11-15 15:11:51 -08:00

64 lines
2.1 KiB
JavaScript

import { ethErrors } from 'eth-rpc-errors';
import { UNSUPPORTED_RPC_METHODS } from '../../../../shared/constants/network';
import handlers from './handlers';
const handlerMap = handlers.reduce((map, handler) => {
for (const methodName of handler.methodNames) {
map.set(methodName, handler);
}
return map;
}, new Map());
/**
* Returns a middleware that implements the RPC methods defined in the handlers
* directory.
*
* The purpose of this middleware is to create portable RPC method
* implementations that are decoupled from the rest of our background
* architecture.
*
* Handlers consume functions that hook into the background, and only depend
* on their signatures, not e.g. controller internals.
*
* Eventually, we'll want to extract this middleware into its own package.
*
* @param {Object} opts - The middleware options
* @returns {(req: Object, res: Object, next: Function, end: Function) => void}
*/
export default function createMethodMiddleware(opts) {
return function methodMiddleware(req, res, next, end) {
// Reject unsupported methods.
if (UNSUPPORTED_RPC_METHODS.has(req.method)) {
return end(ethErrors.rpc.methodNotSupported());
}
const handler = handlerMap.get(req.method);
if (handler) {
const { implementation, hookNames } = handler;
return implementation(req, res, next, end, selectHooks(opts, hookNames));
}
return next();
};
}
/**
* Returns the subset of the specified `hooks` that are included in the
* `hookNames` object. This is a Principle of Least Authority (POLA) measure
* to ensure that each RPC method implementation only has access to the
* API "hooks" it needs to do its job.
*
* @param {Record<string, unknown>} hooks - The hooks to select from.
* @param {Record<string, true>} hookNames - The names of the hooks to select.
* @returns {Record<string, unknown> | undefined} The selected hooks.
*/
function selectHooks(hooks, hookNames) {
if (hookNames) {
return Object.keys(hookNames).reduce((hookSubset, hookName) => {
hookSubset[hookName] = hooks[hookName];
return hookSubset;
}, {});
}
return undefined;
}