Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin APIs and tools for node params (without refreshing) #1051

Merged
merged 11 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions common/result-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,16 @@ const JsonRpcApiResultCode = {
BATCH_TX_MISSING_PROPERTIES: 30404,
BATCH_TX_INVALID_FORMAT: 30405,
BATCH_TX_INVALID_SIGNATURE: 30406,
// ain_addToDevClientApiIpWhitelist
INVALID_IP: 30501,
IP_ALREADY_IN_WHITELIST: 30502,
// ain_removeFromDevClientApiIpWhitelist
IP_NOT_IN_WHITELIST: 30601,
// Admin APIs
ADMIN_FORBIDDEN_REQUEST: 30501,
ADMIN_PARAM_INVALID: 30502,
ADMIN_VALUE_NOT_A_STRING_TYPE: 30503,
ADMIN_ALREADY_IN_WHITELIST: 30504,
ADMIN_NOT_IN_WHITELIST: 30505,
// ain_validateAppName
INVALID_APP_NAME_FOR_STATE_LABEL: 30701,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make sure to run the unit & integration tests? I think they would need some updates from this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for denoting this! I couldn't think these codes were directly used in the tests. Fixed now!

INVALID_APP_NAME_FOR_SERVICE_NAME: 30702,
APP_NAME_ALREADY_IN_USE: 30703,
INVALID_APP_NAME_FOR_STATE_LABEL: 30601,
INVALID_APP_NAME_FOR_SERVICE_NAME: 30602,
APP_NAME_ALREADY_IN_USE: 30603,
};

/**
Expand Down
217 changes: 173 additions & 44 deletions json_rpc/admin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const net = require('net');
const _ = require('lodash');
const {
NodeConfigs,
Expand All @@ -10,124 +9,254 @@ const CommonUtil = require('../common/common-util');
const JsonRpcUtil = require('./json-rpc-util');
const { JSON_RPC_METHODS } = require('./constants');

module.exports = function getApiAccessApis(node) {
function convertValue(valueFromNodeParam, value) {
if (CommonUtil.isBool(valueFromNodeParam)) {
return CommonUtil.convertEnvVarInputToBool(value);
} else if (CommonUtil.isIntegerString(valueFromNodeParam) || CommonUtil.isFloatString(valueFromNodeParam)) {
return Number(value);
} else if (CommonUtil.isArray(valueFromNodeParam) || CommonUtil.isWildcard(valueFromNodeParam)) {
return CommonUtil.getWhitelistFromString(value);
} else {
return value;
}
}

module.exports = function getAdminApis(node) {
return {
[JSON_RPC_METHODS.AIN_GET_DEV_CLIENT_API_IP_WHITELIST]: function(args, done) {
[JSON_RPC_METHODS.AIN_GET_NODE_PARAM]: function(args, done) {
const beginTime = Date.now();
const verified = node.verifyNodeAccountSignature(args.message, args.signature);
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_GET_NODE_PARAM || !verified) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_GET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_FORBIDDEN_REQUEST,
message: `Forbidden request.`
}
}));
return;
}

const param = args.message.param;
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_GET, latency);
if (_.get(args.message, 'method') === JSON_RPC_METHODS.AIN_GET_DEV_CLIENT_API_IP_WHITELIST &&
verified) {
done(null,
JsonRpcUtil.addProtocolVersion({ result: NodeConfigs.DEV_CLIENT_API_IP_WHITELIST }));
if (NodeConfigs[param] === undefined) {
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] does not exist.`
}
}));
} else {
done({ code: 403, message: 'Forbidden' });
done(null, JsonRpcUtil.addProtocolVersion({ result: NodeConfigs[param] }));
}
},

[JSON_RPC_METHODS.AIN_ADD_TO_DEV_CLIENT_API_IP_WHITELIST]: function(args, done) {
[JSON_RPC_METHODS.AIN_SET_NODE_PARAM]: function(args, done) {
const beginTime = Date.now();
const verified = node.verifyNodeAccountSignature(args.message, args.signature);
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_ADD_TO_DEV_CLIENT_API_IP_WHITELIST ||
!verified) {
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_SET_NODE_PARAM || !verified) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done({ code: 403, message: 'Forbidden' });
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_FORBIDDEN_REQUEST,
message: `Forbidden request.`
}
}));
return;
}
if (CommonUtil.isWildcard(args.message.ip)) {
NodeConfigs.DEV_CLIENT_API_IP_WHITELIST = '*';

const param = args.message.param;
if (NodeConfigs[param] === undefined) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Added IP (${args.message.ip}) to whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] does not exist.`
}
}));
return;
}
if (!net.isIPv4(args.message.ip)) {

if (!CommonUtil.isString(args.message.value)) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.INVALID_IP,
message: `Invalid IP: ${args.message.ip}`
code: JsonRpcApiResultCode.ADMIN_VALUE_NOT_A_STRING_TYPE,
message: `(${args.message.value}) is not a string type.)}`
}
}));
return;
}
if (!CommonUtil.isArray(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)) {
// NOTE(liayoo): if the whitelist was "*" previously, adding an IP will no longer "allow-all".
NodeConfigs.DEV_CLIENT_API_IP_WHITELIST = [];

NodeConfigs[param] = convertValue(NodeConfigs[param], args.message.value);
// TODO(kriii): Add a refresher for some params.
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Param [${param}] is now set as: ${JSON.stringify(NodeConfigs[param])}`
}
}));
},

[JSON_RPC_METHODS.AIN_ADD_TO_WHITELIST_NODE_PARAM]: function(args, done) {
const beginTime = Date.now();
const verified = node.verifyNodeAccountSignature(args.message, args.signature);
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_ADD_TO_WHITELIST_NODE_PARAM || !verified) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_FORBIDDEN_REQUEST,
message: `Forbidden request.`
}
}));
return;
}
if (NodeConfigs.DEV_CLIENT_API_IP_WHITELIST.includes(args.message.ip)) {

const param = args.message.param;
if (NodeConfigs[param] === undefined) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.IP_ALREADY_IN_WHITELIST,
message: `IP (${args.message.ip}) already in whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] does not exist.`
}
}));
} else {
NodeConfigs.DEV_CLIENT_API_IP_WHITELIST.push(args.message.ip);
return;
}
if (!CommonUtil.isArray(NodeConfigs[param]) &&
!CommonUtil.isWildcard(NodeConfigs[param])) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Added IP (${args.message.ip}) to whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] is not a whitelist`
}
}));
return;
}

if (!CommonUtil.isString(args.message.value)) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_VALUE_NOT_A_STRING_TYPE,
message: `(${args.message.value}) is not a string type.)}`
}
}));
return;
}

if (!CommonUtil.isArray(NodeConfigs[param])) {
// NOTE(liayoo): if the whitelist was "*" previously, adding an IP will no longer "allow-all".
NodeConfigs[param] = [];
}
if (NodeConfigs[param].includes(args.message.value)) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_ALREADY_IN_WHITELIST,
message: `(${args.message.value}) already in whitelist [${param}]: ${JSON.stringify(NodeConfigs[param])}`
}
}));
return;
}

if (CommonUtil.isWildcard(args.message.value)) {
NodeConfigs[param] = '*';
} else {
NodeConfigs[param].push(args.message.value);
}
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Added (${args.message.value}) to whitelist [${param}]: ${JSON.stringify(NodeConfigs[param])}`
}
}));
},

[JSON_RPC_METHODS.AIN_REMOVE_FROM_DEV_CLIENT_API_IP_WHITELIST]: function(args, done) {
[JSON_RPC_METHODS.AIN_REMOVE_FROM_WHITELIST_NODE_PARAM]: function(args, done) {
const beginTime = Date.now();
const verified = node.verifyNodeAccountSignature(args.message, args.signature);
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_REMOVE_FROM_DEV_CLIENT_API_IP_WHITELIST ||
!verified) {
if (_.get(args.message, 'method') !== JSON_RPC_METHODS.AIN_REMOVE_FROM_WHITELIST_NODE_PARAM || !verified) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done({ code: 403, message: 'Forbidden' });
return;
}
if (CommonUtil.isWildcard(args.message.ip)
&& CommonUtil.isWildcard(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)) {
NodeConfigs.DEV_CLIENT_API_IP_WHITELIST = [];

const param = args.message.param;
if (NodeConfigs[param] === undefined) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] does not exist.`
}
}));
return;
}
if (!CommonUtil.isArray(NodeConfigs[param]) &&
!CommonUtil.isWildcard(NodeConfigs[param])) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.ADMIN_PARAM_INVALID,
message: `Param [${param}] is not a whitelist`
}
}));
return;
}

if (!CommonUtil.isString(args.message.value)) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Removed IP (${args.message.ip}) from whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
code: JsonRpcApiResultCode.ADMIN_VALUE_NOT_A_STRING_TYPE,
message: `(${args.message.value}) is not a string type.)}`
}
}));
return;
}
if (!CommonUtil.isArray(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST) ||
!NodeConfigs.DEV_CLIENT_API_IP_WHITELIST.includes(args.message.ip)) {

if (CommonUtil.isWildcard(args.message.value)) {
NodeConfigs[param] = [];
} else if (!CommonUtil.isArray(NodeConfigs[param]) ||
!NodeConfigs[param].includes(args.message.value)) {
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.IP_NOT_IN_WHITELIST,
message: `IP (${args.message.ip}) not in whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
code: JsonRpcApiResultCode.ADMIN_NOT_IN_WHITELIST,
message: `(${args.message.value}) not in whitelist [${param}]: ${JSON.stringify(NodeConfigs[param])}`
}
}));
return;
} else {
NodeConfigs[param] = NodeConfigs[param].filter((value) => value !== args.message.value);
}
NodeConfigs.DEV_CLIENT_API_IP_WHITELIST = NodeConfigs.DEV_CLIENT_API_IP_WHITELIST
.filter((ip) => ip !== args.message.ip);
const latency = Date.now() - beginTime;
trafficStatsManager.addEvent(TrafficEventTypes.ACCESS_CONTROL_SET, latency);
done(null, JsonRpcUtil.addProtocolVersion({
result: {
code: JsonRpcApiResultCode.SUCCESS,
message: `Removed IP (${args.message.ip}) from whitelist: ${JSON.stringify(NodeConfigs.DEV_CLIENT_API_IP_WHITELIST)}`
message: `Removed (${args.message.value}) from whitelist [${param}]: ${JSON.stringify(NodeConfigs[param])}`
}
}));
},
Expand Down
14 changes: 8 additions & 6 deletions json_rpc/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const JSON_RPC_METHODS = {
AIN_ADD_TO_DEV_CLIENT_API_IP_WHITELIST: 'ain_addToDevClientApiIpWhitelist',
AIN_ADD_TO_WHITELIST_NODE_PARAM: 'ain_addToWhitelistNodeParam',
AIN_CHECK_PROTOCOL_VERSION: 'ain_checkProtocolVersion',
AIN_EVAL_RULE: 'ain_evalRule',
AIN_EVAL_OWNER: 'ain_evalOwner',
Expand All @@ -13,11 +13,11 @@ const JSON_RPC_METHODS = {
AIN_GET_BLOCK_TRANSACTION_COUNT_BY_HASH: 'ain_getBlockTransactionCountByHash',
AIN_GET_BLOCK_TRANSACTION_COUNT_BY_NUMBER: 'ain_getBlockTransactionCountByNumber',
AIN_GET_BOOTSTRAP_PUB_KEY: 'ain_getBootstrapPubKey',
AIN_GET_DEV_CLIENT_API_IP_WHITELIST: 'ain_getDevClientApiIpWhitelist',
AIN_GET_EVENT_HANDLER_CHANNEL_INFO: 'ain_getEventHandlerChannelInfo',
AIN_GET_EVENT_HANDLER_FILTER_INFO: 'ain_getEventHandlerFilterInfo',
AIN_GET_LAST_BLOCK: 'ain_getLastBlock',
AIN_GET_LAST_BLOCK_NUMBER: 'ain_getLastBlockNumber',
AIN_GET_NODE_PARAM: 'ain_getNodeParam',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add tool scripts for the new apis?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New tools added!

AIN_GET_NONCE: 'ain_getNonce',
AIN_GET_PENDING_TRANSACTIONS: 'ain_getPendingTransactions',
AIN_GET_PROOF_HASH: 'ain_getProofHash',
Expand All @@ -41,9 +41,10 @@ const JSON_RPC_METHODS = {
AIN_MATCH_FUNCTION: 'ain_matchFunction',
AIN_MATCH_OWNER: 'ain_matchOwner',
AIN_MATCH_RULE: 'ain_matchRule',
AIN_REMOVE_FROM_DEV_CLIENT_API_IP_WHITELIST: 'ain_removeFromDevClientApiIpWhitelist',
AIN_REMOVE_FROM_WHITELIST_NODE_PARAM: 'ain_removeFromWhitelistNodeParam',
AIN_SEND_SIGNED_TRANSACTION: 'ain_sendSignedTransaction',
AIN_SEND_SIGNED_TRANSACTION_BATCH: 'ain_sendSignedTransactionBatch',
AIN_SET_NODE_PARAM: 'ain_setNodeParam',
AIN_VALIDATE_APP_NAME: 'ain_validateAppName',
NET_CONSENSUS_STATUS: 'net_consensusStatus',
NET_GET_CHAIN_ID: 'net_getChainId',
Expand All @@ -57,13 +58,14 @@ const JSON_RPC_METHODS = {
}

const JSON_RPC_SET_METHOD_SET = new Set([
JSON_RPC_METHODS.AIN_ADD_TO_DEV_CLIENT_API_IP_WHITELIST,
JSON_RPC_METHODS.AIN_ADD_TO_WHITELIST_NODE_PARAM,
JSON_RPC_METHODS.AIN_INJECT_ACCOUNT_FROM_HD_WALLET,
JSON_RPC_METHODS.AIN_INJECT_ACCOUNT_FROM_KEYSTORE,
JSON_RPC_METHODS.AIN_INJECT_ACCOUNT_FROM_PRIVATE_KEY,
JSON_RPC_METHODS.AIN_REMOVE_FROM_DEV_CLIENT_API_IP_WHITELIST,
JSON_RPC_METHODS.AIN_REMOVE_FROM_WHITELIST_NODE_PARAM,
JSON_RPC_METHODS.AIN_SEND_SIGNED_TRANSACTION,
JSON_RPC_METHODS.AIN_SEND_SIGNED_TRANSACTION_BATCH
JSON_RPC_METHODS.AIN_SEND_SIGNED_TRANSACTION_BATCH,
JSON_RPC_METHODS.AIN_SET_NODE_PARAM
]);

module.exports = {
Expand Down
Loading