k2hr3-api
Version:
K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules
1,054 lines • 123 kB
JavaScript
"use strict";
/*
* K2HR3 REST API
*
* Copyright 2017 Yahoo Japan Corporation.
*
* K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
* common management information for the cloud.
* K2HR3 can dynamically manage information as "who", "what", "operate".
* These are stored as roles, resources, policies in K2hdkc, and the
* client system can dynamically read and modify these information.
*
* For the full copyright and license information, please view
* the license file that was distributed with this source code.
*
* AUTHOR: Takeshi Nakatani
* CREATE: Wed Jun 8 2017
* REVISION:
*
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const k2hr3apiutil_1 = __importDefault(require("../lib/k2hr3apiutil"));
const k2hr3resutil_1 = __importDefault(require("../lib/k2hr3resutil"));
const k2hr3tokens_1 = __importDefault(require("../lib/k2hr3tokens"));
const k2hr3userdata_1 = __importDefault(require("../lib/k2hr3userdata"));
const k2hr3dkc_1 = __importDefault(require("../lib/k2hr3dkc"));
const dbglogging_1 = __importDefault(require("../lib/dbglogging"));
const express_1 = __importDefault(require("express"));
const k2hr3config_1 = require("../lib/k2hr3config");
const k2hr3keys_1 = require("../lib/k2hr3keys");
const apiConf = new k2hr3config_1.r3ApiConfig();
const r3keys = k2hr3keys_1.getK2hr3Keys;
const router = express_1.default.Router();
//
// check typs
//
const rawIsValTypeQueryRoleHostBaseValue = (val) => {
if (!k2hr3apiutil_1.default.isPlainObject(val)) {
return false;
}
const _obj = val;
const _isStringOrNull = (key) => (null === _obj[key] || k2hr3apiutil_1.default.isString(_obj[key]));
const _isStringOrNullOrUndef = (key) => (undefined === _obj[key] || null === _obj[key] || k2hr3apiutil_1.default.isString(_obj[key]));
const _isStrNumOrNullOrUndef = (key) => (undefined === _obj[key] || null === _obj[key] || k2hr3apiutil_1.default.isString(_obj[key]) || k2hr3apiutil_1.default.isSafeNumber(_obj[key]));
return (_isStringOrNull('host') &&
_isStrNumOrNullOrUndef('port') &&
_isStringOrNullOrUndef('cuk') &&
_isStringOrNullOrUndef('extra') &&
_isStringOrNullOrUndef('tag') &&
_isStringOrNullOrUndef('inboundip') &&
_isStringOrNullOrUndef('outboundip'));
};
const rawIsValTypeQueryRoleHostBaseValueArray = (arr) => {
return (Array.isArray(arr) && arr.every((element) => rawIsValTypeQueryRoleHostBaseValue(element)));
};
//---------------------------------------------------------
// Configuration
// * Get role full path which is allowed to remove ip address
// * Get expiration for role tokens
//---------------------------------------------------------
let delhost_role_yrn = null;
let expire_rtoken = 0;
let expire_reg_rtoken = 0;
(() => {
const admincfgobj = apiConf.getK2hr3AdminConfig();
if (k2hr3apiutil_1.default.isPlainObject(admincfgobj) && k2hr3apiutil_1.default.isSafeString(admincfgobj.tenant) && k2hr3apiutil_1.default.isSafeString(admincfgobj.delhostrole)) {
const keys = r3keys(null, admincfgobj.tenant.trim());
delhost_role_yrn = keys.ROLE_TOP_KEY + ':' + admincfgobj.delhostrole.trim();
}
else {
dbglogging_1.default.elog('Could not find tenant/role in configuration for deleting host by cuk.');
delhost_role_yrn = null;
}
expire_rtoken = apiConf.getExpireTimeRoleToken();
expire_reg_rtoken = apiConf.getExpireTimeRegRoleToken();
})();
//---------------------------------------------------------
// Router POST
//---------------------------------------------------------
//
// Mountpath : '/v1/role'
// POST '/v1/role{/<role{/...}>}' : post role on version 1
// HEADER : X-Auth-Token => User token or Role token
// response body : result => true/false
// message => messages
//
// This mount point is for creating(update) role or creating(update) host in role.
//
router.post('/', (req, res, next) => {
dbglogging_1.default.dlog('CALL:', req.method, req.url, req.baseUrl);
res.type('application/json; charset=utf-8');
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isSafeString(req.baseUrl)) {
const result = {
result: false,
message: 'POST request or url or query is wrong'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// check api type
if ('/v1/role' === decodeURI(req.baseUrl)) {
//------------------------------
// create role type
//------------------------------
postRole(req, res, next);
}
else {
// check host api
const keys = r3keys();
const requestptn = new RegExp(keys.MATCH_URI_GET_ROLE_DATA); // regex = /^\/v1\/role\/(.*)/
const reqmatchs = decodeURI(req.baseUrl).match(requestptn);
if (!k2hr3apiutil_1.default.isStringArray(reqmatchs) || !k2hr3apiutil_1.default.isNotEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === k2hr3apiutil_1.default.getSafeString(reqmatchs[1])) {
const result = {
result: false,
message: 'POST request url does not have role name'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// role name
const name = reqmatchs[1].toLowerCase();
//------------------------------
// create host type
//------------------------------
postRoleHost(name, req, res, next);
}
});
//---------------------------------------------------------
// Router PUT
//---------------------------------------------------------
// Mountpath : '/v1/role'
// PUT '/v1/role{/<role{/...}>}': put role on version 1
// HEADER : X-Auth-Token => User token or Role token
// response body : result => true/false
// message => messages
//
// This mount point is for creating(update) role and creating(update) host in role.
//
router.put('/', (req, res, next) => {
dbglogging_1.default.dlog('CALL:', req.method, req.url, req.baseUrl);
res.type('application/json; charset=utf-8');
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isSafeString(req.baseUrl)) {
const result = {
result: false,
message: 'PUT request or url or query is wrong'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// check api type
if ('/v1/role' === decodeURI(req.baseUrl)) {
//------------------------------
// create role type
//------------------------------
putRole(req, res, next);
}
else {
// check host api
const keys = r3keys();
const requestptn = new RegExp(keys.MATCH_URI_GET_ROLE_DATA); // regex = /^\/v1\/role\/(.*)/
const reqmatchs = decodeURI(req.baseUrl).match(requestptn);
if (!k2hr3apiutil_1.default.isStringArray(reqmatchs) || !k2hr3apiutil_1.default.isNotEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === k2hr3apiutil_1.default.getSafeString(reqmatchs[1])) {
const result = {
result: false,
message: 'POST request url does not have role name'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// role name
const name = reqmatchs[1].toLowerCase();
//------------------------------
// create host type
//------------------------------
putRoleHost(name, req, res, next);
}
});
//
// Sub router function for POST CREATE ROLE
//
// Mountpath : '/v1/role'
// POST '/v1/role' : post role on version 1
// HEADER : X-Auth-Token => User token
// response body : result => true/false
// message => messages
// body :
// {
// "role": {
// "name": <role name> => key is "yrn:yahoo:<service>::<tenant>:role:<role>"
// <role> can include '/' for hierarchical path
// "policies": [<policy yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:role:<role>/policies"
// specify policy as "yrn:yahoo:<service>::<tenant>:policy:<policy>"
// if null or undefined is specified, not update this member in role when this role exists.
// if '' or zero array, this member in role is set empty array.
// "alias": [<role yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:role:<role>/@"
// specify another role as "yrn:yahoo:<service>::<tenant>:role:<role>"
// if null or undefined is specified, not update this member in role when this role exists.
// if '' or zero array, this member in role is set empty array.
// }
// }
//
// [NOTE]
// This API does not set host into roles as initial. You can add host to role
// by another API which is an API dedicated to adding host.
//
const postRole = (req, res, _) => {
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isPlainObject(req.body) ||
!k2hr3apiutil_1.default.isPlainObject(req.body.role)) {
const result = {
result: false,
message: 'POST body does not have role data'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check token
//------------------------------
const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token
if (!token_result.result) {
const result = {
result: token_result.result,
message: k2hr3apiutil_1.default.getSafeString(token_result.message),
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, token_result.status, result);
return;
}
const token_info = token_result.token_info;
if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) {
const result = {
result: false,
message: 'specified wrong token or it is not scoped user token'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check arguments
//------------------------------
// name
if (!k2hr3apiutil_1.default.isSafeString(req.body.role.name)) {
const result = {
result: false,
message: 'role:name field is wrong : ' + JSON.stringify(req.body.role.name)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const keys = r3keys(token_info.user, token_info.tenant);
let name = k2hr3apiutil_1.default.getSafeString(req.body.role.name).toLowerCase();
// role name is only name or full yrn path
let nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/
const namematchs = name.match(nameptn);
if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) {
name = namematchs[1];
}
// check name which is not full yrn
nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/
if (name.match(nameptn)) {
const result = {
result: false,
message: 'POST request query has wrong yrn full path to role'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// policies
const policiesptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/
const policiespram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.role.policies, policiesptn, null);
if (false === policiespram.result) {
const result = {
result: false,
message: 'role:policies field is wrong : ' + JSON.stringify(req.body.role.policies)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const policies = policiespram.parameter;
// alias
const aliasptn = new RegExp('^' + keys.MATCH_ANY_TENANT_ROLE); // regex = /^yrn:yahoo:(.*)::(.*):role:(.*)/
const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.role.alias, aliasptn, null);
if (false === aliaspram.result) {
const result = {
result: false,
message: 'role:alias field is wrong : ' + JSON.stringify(req.body.role.alias)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const aliases = aliaspram.parameter;
//------------------------------
// set all field to role
//------------------------------
const role_result = k2hr3dkc_1.default.setRoleAll(token_info.user, token_info.tenant, name, policies, aliases, null, false, null, false);
if (!k2hr3apiutil_1.default.isPlainObject(role_result) || !k2hr3apiutil_1.default.isBoolean(role_result.result) || false === role_result.result) {
if (!k2hr3apiutil_1.default.isPlainObject(role_result)) {
const result = {
result: false,
message: 'Could not get response from setRoleAll'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
else {
const result = {
result: k2hr3apiutil_1.default.isBoolean(role_result.result) ? role_result.result : false,
message: k2hr3apiutil_1.default.isString(role_result.message) ? role_result.message : 'Could not get error message in response from setRoleAll'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
return;
}
dbglogging_1.default.dlog('succeed : ' + k2hr3apiutil_1.default.getSafeString(role_result.message));
res.status(201); // 201: Created
res.send(JSON.stringify(role_result));
};
//
// Sub router function for PUT CREATE ROLE
//
// Mountpath : '/v1/role'
// PUT '/v1/role{/<role{/...}>}' : put role on version 1
// HEADER : X-Auth-Token => User token
// response body : result => true/false
// message => messages
// url argument
// "name": <role name> => key is "yrn:yahoo:<service>::<tenant>:role:<role>"
// <role> can include '/' for hierarchical path
// "policies": [<policy yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:role:<role>/policies"
// specify policy as "yrn:yahoo:<service>::<tenant>:policy:<policy>", it is formatted by JSON.
// if null or undefined is specified, not update this member in role when this role exists.
// if '' or zero array, this member in role is set empty array.
// "alias": [<role yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:role:<role>/@"
// specify another role as "yrn:yahoo:<service>::<tenant>:role:<role>", it is formatted by JSON.
// if null or undefined is specified, not update this member in role when this role exists.
// if '' or zero array, this member in role is set empty array.
//
// [NOTE]
// This API does not set host into roles as initial. You can add host to role
// by another API which is an API dedicated to adding host.
//
const putRole = (req, res, _) => {
dbglogging_1.default.dlog('CALL:', req.method, req.url);
res.type('application/json; charset=utf-8');
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isPlainObject(req.query)) {
const result = {
result: false,
message: 'PUT argument does not have any data'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check token
//------------------------------
const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token
if (!token_result.result) {
const result = {
result: token_result.result,
message: k2hr3apiutil_1.default.getSafeString(token_result.message),
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, token_result.status, result);
return;
}
const token_info = token_result.token_info;
if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) {
const result = {
result: false,
message: 'specified wrong token or it is not scoped user token'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check arguments
//------------------------------
// name
if (!k2hr3apiutil_1.default.isSafeString(req.query.name)) {
const result = {
result: false,
message: 'role:name field is wrong : ' + JSON.stringify(req.query.name)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const keys = r3keys(token_info.user, token_info.tenant);
let name = k2hr3apiutil_1.default.getSafeString(req.query.name).toLowerCase();
// role name is only name or full yrn path
let nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/
const namematchs = name.match(nameptn);
if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) {
name = namematchs[1];
}
// check name which is not full yrn
nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/
if (name.match(nameptn)) {
const result = {
result: false,
message: 'PUT request query has wrong yrn full path to role'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// policies
let policies = null;
if (!k2hr3apiutil_1.default.isString(req.query.policies)) {
policies = null;
}
else if ('' === req.query.policies) {
policies = '';
}
else if (k2hr3apiutil_1.default.isSafeString(req.query.policies)) {
// policies is encoded by JSON, this value is array.
//
let tmp_pol = k2hr3apiutil_1.default.getSafeString(req.query.policies);
if (k2hr3apiutil_1.default.checkSimpleJSON(tmp_pol)) {
tmp_pol = JSON.parse(tmp_pol);
}
const policiesptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/
const policiespram = k2hr3apiutil_1.default.getNormalizeParameter(tmp_pol, policiesptn, null);
if (false === policiespram.result) {
const result = {
result: false,
message: 'role:policies field is wrong : ' + req.query.policies
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
policies = policiespram.parameter ?? null;
}
// alias
let aliases = null;
if (!k2hr3apiutil_1.default.isString(req.query.alias)) {
aliases = null;
}
else if ('' === req.query.alias) {
aliases = '';
}
else if (k2hr3apiutil_1.default.isSafeString(req.query.alias)) {
// alias is encoded by JSON, this value is array.
//
let tmp_aliases = k2hr3apiutil_1.default.getSafeString(req.query.alias);
if (k2hr3apiutil_1.default.checkSimpleJSON(tmp_aliases)) {
tmp_aliases = JSON.parse(tmp_aliases);
}
const aliasptn = new RegExp('^' + keys.MATCH_ANY_TENANT_ROLE); // regex = /^yrn:yahoo:(.*)::(.*):role:(.*)/
const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(tmp_aliases, aliasptn, null);
if (false === aliaspram.result) {
const result = {
result: false,
message: 'role:alias field is wrong : ' + req.query.alias
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
aliases = aliaspram.parameter ?? null;
}
//------------------------------
// set all field to role
//------------------------------
const role_result = k2hr3dkc_1.default.setRoleAll(token_info.user, token_info.tenant, name, policies, aliases, null, false, null, false);
if (!k2hr3apiutil_1.default.isPlainObject(role_result) || !k2hr3apiutil_1.default.isBoolean(role_result.result) || false === role_result.result) {
if (!k2hr3apiutil_1.default.isPlainObject(role_result)) {
const result = {
result: false,
message: 'Could not get response from setRoleAll'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
else {
const result = {
result: k2hr3apiutil_1.default.isBoolean(role_result.result) ? role_result.result : false,
message: k2hr3apiutil_1.default.isString(role_result.message) ? role_result.message : 'Could not get error message in response from setRoleAll'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
return;
}
dbglogging_1.default.dlog('succeed : ' + k2hr3apiutil_1.default.getSafeString(role_result.message));
res.status(201); // 201: Created
res.send(JSON.stringify(role_result));
};
//
// Sub router function for POST CREATE HOST
//
// Mountpath : '/v1/role'
// POST '/v1/role/<role{/...}>' : post role on version 1
// HEADER : X-Auth-Token => User token or Role token
// response body : result => true/false
// message => messages
//
// [UserToken] body :
// {
// "host": { => specified single host
// "host": <hostname / ip address> => key is for "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/{name, ip}/<hostname port cuk>"
// "port": <port number> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is number string(0-), allowed null and '' for this value.
// if this value is '0', it means any port.
// "cuk": <container unique key> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is string. if this value is undefined/null/empty string, it means any.
// "extra": <extra string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// extra is any string including Control code, allowed null and '' for this value.
// "tag": <string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// tag is any string including Control code, allowed null and '' for this value.
// "inboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// inboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// "outboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// outboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// }
// "clear_hostname": <true/false>
// "clear_ips": <true/false>
// }
// or
// {
// "host": [ => specified host as Array(only POST request has this type)
// {
// "host": <hostname / ip address>
// "port": <port number>
// "cuk": <container unique key>
// "extra": <extra string data>
// "tag": <string data>
// "inboundip": <ip address>
// "outboundip": <ip address>
// }
// ...
// ]
// "clear_hostname": <true/false>
// "clear_ips": <true/false>
// }
//
// [RoleToken] body :
// {
// "host": {
// "port": <port number> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/ip/<ip port cuk>"
// this value is number string(0-), allowed null and '' for this value.
// if this value is '0', it means any port.
// "cuk": <container unique key> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is string. if this value is undefined/null/empty string, it means any.
// "extra": <extra string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// extra is any string including Control code, allowed null and '' for this value.
// "tag": <string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// tag is any string including Control code, allowed null and '' for this value.
// "inboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// inboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// "outboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// outboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// }
// }
//
// [NOTE]
// This API only set(add/create) host into role. The host is specified hostname.
// The hostname is any string as like hostname.(ex. "x.yahoo.co.jp", "x[0-9].yahoo.co.jp", "*.yahoo.co.jp", "*", "(.*)", etc)
// If port number is 0, it means any port.
// If cuk is undefined/null/empty string, it means any.
// Extra data can include control-code(CR, etc).
//
const postRoleHost = (role, req, res, _) => {
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isPlainObject(req.body) ||
(!k2hr3apiutil_1.default.isArray(req.body.host) && !k2hr3apiutil_1.default.isPlainObject(req.body.host))) {
const result = {
result: false,
message: 'POST body does not have host data'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check token
//------------------------------
const token_result = k2hr3tokens_1.default.checkToken(req, true); // scoped, both token
if (!token_result.result) {
const result = {
result: token_result.result,
message: k2hr3apiutil_1.default.getSafeString(token_result.message),
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, token_result.status, result);
return;
}
const token_info = token_result.token_info;
if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) {
const result = {
result: false,
message: 'specified wrong token or it is not scoped user token'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const is_host_req = (!k2hr3apiutil_1.default.isArray(req.body.host) && (!k2hr3apiutil_1.default.isPlainObject(req.body.host) || !k2hr3apiutil_1.default.isSafeString(req.body.host.host)));
const keys = r3keys(token_info.user, token_info.tenant);
//------------------------------
// check arguments
//------------------------------
// role name check
let name = k2hr3apiutil_1.default.getSafeString(role).toLowerCase();
let nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/
const namematchs = name.match(nameptn);
if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) {
// name is full yrn, then reset only name.
name = namematchs[1];
}
else {
// role name is not full yrn, then check other yrn path
nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/
if (name.match(nameptn)) {
const result = {
result: false,
message: 'POST request url has wrong yrn full path to role'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
}
//------------------------------
// build parameters
//------------------------------
let role_result;
if (!is_host_req) {
//
// request from user token
//
let hostArray = [];
if (rawIsValTypeQueryRoleHostBaseValueArray(req.body.host)) {
hostArray = req.body.host;
}
else if (rawIsValTypeQueryRoleHostBaseValue(req.body.host)) {
hostArray = [req.body.host];
}
// check array and make ip array
let hostnameArray = [];
let ipArray = [];
for (let cnt = 0; cnt < hostArray.length; ++cnt) {
if (!k2hr3apiutil_1.default.isSafeString(hostArray[cnt].host)) {
const result = {
result: false,
message: 'host is not specified.'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// hostname or ip address
const tmp_host = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].host);
let tg_host = null;
let tg_ip = null;
if (k2hr3apiutil_1.default.isIpAddressString(tmp_host)) {
tg_ip = tmp_host.toLowerCase();
tg_host = null;
}
else if (k2hr3apiutil_1.default.isSafeString(tmp_host)) {
tg_host = tmp_host.toLowerCase();
tg_ip = null;
}
// port
let port = 0;
if (k2hr3apiutil_1.default.isSafeNumeric(hostArray[cnt].port)) {
const port_tmp = k2hr3apiutil_1.default.cvtToNumber(hostArray[cnt].port);
if (!k2hr3apiutil_1.default.isSafeNumber(port_tmp)) {
const result = {
result: false,
message: 'POST request has port which is not number: ' + JSON.stringify(hostArray[cnt].port)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
port = port_tmp;
}
// cuk
const tmp_cuk = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].cuk).trim();
let cuk = null;
if (k2hr3apiutil_1.default.isSafeString(tmp_cuk)) {
cuk = tmp_cuk;
}
// extra
const tmp_extra = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].extra);
let extra = null;
if (k2hr3apiutil_1.default.isSafeString(tmp_extra)) {
extra = tmp_extra;
}
// tag
const tmp_tag = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].tag);
let tag = null;
if (k2hr3apiutil_1.default.isSafeString(tmp_tag)) {
tag = tmp_tag;
}
// set base host information
const host_info = {
hostname: tg_host,
ip: tg_ip,
port: port,
cuk: cuk,
extra: extra,
tag: tag,
inboundip: null,
outboundip: null
};
// set optional keys
if (k2hr3apiutil_1.default.isSafeString(hostArray[cnt].inboundip)) {
if (!k2hr3apiutil_1.default.isIpAddressString(hostArray[cnt].inboundip)) {
const result = {
result: false,
message: 'POST request has inbound ip address which is not ignore ip address string: ' + JSON.stringify(hostArray[cnt].inboundip)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
host_info.inboundip = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].inboundip);
}
if (k2hr3apiutil_1.default.isSafeString(hostArray[cnt].outboundip)) {
if (!k2hr3apiutil_1.default.isIpAddressString(hostArray[cnt].outboundip)) {
const result = {
result: false,
message: 'POST request has outbound ip address which is not ignore ip address string: ' + JSON.stringify(hostArray[cnt].outboundip)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
host_info.outboundip = k2hr3apiutil_1.default.getSafeString(hostArray[cnt].outboundip);
}
// push array
if (null !== tg_host) {
hostnameArray.push(host_info);
}
if (null !== tg_ip) {
ipArray.push(host_info);
}
}
if (!k2hr3apiutil_1.default.isNotEmptyArray(hostnameArray)) {
hostnameArray = null;
}
if (!k2hr3apiutil_1.default.isNotEmptyArray(ipArray)) {
ipArray = null;
}
// flags
let clear_hostname = false;
let clear_ips = false;
if (k2hr3apiutil_1.default.isBoolean(req.body.clear_hostname)) {
clear_hostname = req.body.clear_hostname;
}
if (k2hr3apiutil_1.default.isBoolean(req.body.clear_ips)) {
clear_ips = req.body.clear_ips;
}
//
// Add hostnames and ips ---> Need User Token
//
role_result = k2hr3dkc_1.default.updateRoleHosts(token_info.user, token_info.tenant, name, hostnameArray, clear_hostname, ipArray, clear_ips);
}
else {
//
// request from host(token)
//
// get ip address
const ip = k2hr3apiutil_1.default.getClientIpAddress(req);
if (!k2hr3apiutil_1.default.isSafeString(ip)) {
const result = {
result: false,
message: 'Could not get ip address from request.'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
// port
let port = 0;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeNumeric(req.body.host.port)) {
const port_tmp = k2hr3apiutil_1.default.cvtToNumber(req.body.host.port);
if (!k2hr3apiutil_1.default.isSafeNumber(port_tmp)) {
const result = {
result: false,
message: 'POST request has port which is not number: ' + JSON.stringify(req.body.host.port)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
port = port_tmp;
}
// cuk
let cuk = null;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeString(req.body.host.cuk) && k2hr3apiutil_1.default.isSafeString(req.body.host.cuk.trim())) {
cuk = k2hr3apiutil_1.default.getSafeString(req.body.host.cuk).trim();
}
// extra
let extra = null;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeString(req.body.host.extra)) {
const extra_tmp = k2hr3apiutil_1.default.getSafeString(req.body.host.extra);
if (k2hr3apiutil_1.default.checkSimpleJSON(extra_tmp)) {
const extra_parsed_tmp = JSON.parse(extra_tmp);
if (!k2hr3apiutil_1.default.isSafeString(extra_parsed_tmp)) {
const result = {
result: false,
message: 'POST request has extra which is not string: ' + JSON.stringify(req.body.host.extra)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
extra = extra_parsed_tmp;
}
else {
extra = extra_tmp;
}
}
// tag
let tag = null;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeString(req.body.host.tag)) {
const tag_tmp = k2hr3apiutil_1.default.getSafeString(req.body.host.tag);
if (k2hr3apiutil_1.default.checkSimpleJSON(tag_tmp)) {
const tag_parsed_tmp = JSON.parse(tag_tmp);
if (!k2hr3apiutil_1.default.isSafeString(tag_parsed_tmp)) {
const result = {
result: false,
message: 'POST request has tag which is not string: ' + JSON.stringify(req.body.host.tag)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
tag = tag_parsed_tmp;
}
else {
tag = tag_tmp;
}
}
// inboundip(optional)
let inboundip = null;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeString(req.body.host.inboundip)) {
if (!k2hr3apiutil_1.default.isIpAddressString(req.body.host.inboundip)) {
const result = {
result: false,
message: 'POST request has inbound ip address which is not ignore ip address string: ' + JSON.stringify(req.body.host.inboundip)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
inboundip = k2hr3apiutil_1.default.getSafeString(req.body.host.inboundip);
}
// outboundip(optional)
let outboundip = null;
if (k2hr3apiutil_1.default.isPlainObject(req.body.host) && k2hr3apiutil_1.default.isSafeString(req.body.host.outboundip)) {
if (!k2hr3apiutil_1.default.isIpAddressString(req.body.host.outboundip)) {
const result = {
result: false,
message: 'POST request has outbound ip address which is not ignore ip address string: ' + JSON.stringify(req.body.host.outboundip)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
outboundip = k2hr3apiutil_1.default.getSafeString(req.body.host.outboundip);
}
//
// Add ip address ---> Role Token or User Token
//
role_result = k2hr3dkc_1.default.addHost(token_info.tenant, name, null, ip, port, cuk, extra, tag, inboundip, outboundip);
}
//------------------------------
// check result
//------------------------------
if (!k2hr3apiutil_1.default.isPlainObject(role_result) || !k2hr3apiutil_1.default.isBoolean(role_result.result) || false === role_result.result) {
const mode_type = (is_host_req ? 'addHost' : 'updateRoleHosts');
if (!k2hr3apiutil_1.default.isPlainObject(role_result)) {
const result = {
result: false,
message: ('Could not get response from ' + mode_type)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
else {
const result = {
result: false,
message: k2hr3apiutil_1.default.isString(role_result.message) ? role_result.message : ('Could not get error message in response from ' + mode_type)
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
}
return;
}
dbglogging_1.default.dlog('succeed : ' + k2hr3apiutil_1.default.getSafeString(role_result.message));
res.status(201); // 201: Created
res.send(JSON.stringify(role_result));
};
//
// Sub router function for PUT CREATE HOST
//
// Mountpath : '/v1/role'
// PUT '/v1/role/<role{/...}>' : put role on version 1
// HEADER : X-Auth-Token => User token or Role token
// response body : result => true/false
// message => messages
//
// [UserToken] url argument
// "host": <hostname or ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/{name, ip}/<hostname port cuk>"
// "port": <port number> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is number string(0-), allowed null and '' for this value.
// if this value is '0', it means any port.
// "cuk": <container unique key> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is string. if this value is undefined/null/empty string, it means any.
// "extra": <extra string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// This value must be encoded by JSON.
// extra is any string including Control code, allowed null and '' for this value.
// "tag": <string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// This value must be encoded by JSON.
// tag is any string including Control code, allowed null and '' for this value.
// "inboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// inboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// "outboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// outboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
//
// [RoleToken] url argument
// "port": <port number> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/ip/<ip port cuk>"
// this value is number string(0-), allowed null and '' for this value.
// if this value is '0', it means any port.
// "cuk": <container unique key> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/name/<hostname port cuk>"
// this value is string. if this value is undefined/null/empty string, it means any.
// "extra": <extra string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// This value must be encoded by JSON.
// extra is any string including Control code, allowed null and '' for this value.
// "tag": <string data> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// This value must be encoded by JSON.
// tag is any string including Control code, allowed null and '' for this value.
// "inboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// inboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
// "outboundip": <ip address> => key is "yrn:yahoo:<service>::<tenant>:role:<role>/hosts/..."
// outboundip is set ip address string. if you do not use proxy/gateway/bridge/etc, you do not need to set this key.
//
// [NOTE]
// This API only set(add/create) host into role. Ether hostname or ip address must be specified.
// If port number is 0, it means any port.
// If cuk is undefined/null/empty string, it means any.
// Extra data can include control-code(CR, etc).
//
const putRoleHost = (role, req, res, _) => {
dbglogging_1.default.dlog('CALL:', req.method, req.url);
res.type('application/json; charset=utf-8');
if (!k2hr3apiutil_1.default.isPlainObject(req) ||
!k2hr3apiutil_1.default.isPlainObject(req.query)) {
const result = {
result: false,
message: 'PUT argument does not have any data'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
//------------------------------
// check token
//------------------------------
const token_result = k2hr3tokens_1.default.checkToken(req, true); // scoped, both token
if (!token_result.result) {
const result = {
result: token_result.result,
message: k2hr3apiutil_1.default.getSafeString(token_result.message),
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, token_result.status, result);
return;
}
const token_info = token_result.token_info;
if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) {
const result = {
result: false,
message: 'specified wrong token or it is not scoped user token'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const is_host_req = !k2hr3apiutil_1.default.isSafeString(req.query.host);
const keys = r3keys(token_info.user, token_info.tenant);
//------------------------------
// check arguments
//------------------------------
// role name check
let name = k2hr3apiutil_1.default.getSafeString(role).toLowerCase();
let nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/
const namematchs = name.match(nameptn);
if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) {
// name is full yrn, then reset only name.
name = namematchs[1];
}
else {
// role name is not full yrn, then check other yrn path
nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/
if (name.match(nameptn)) {
const result = {
result: false,
message: 'POST request url has wrong yrn full path to role'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
}
// hostname
let hostname = null;
let ip = null;
if (!is_host_req) {
if (!k2hr3apiutil_1.default.isSafeString(req.query.host)) {
const result = {
result: false,
message: 'host is not specified.'
};
dbglogging_1.default.elog(result.message);
k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request
return;
}
const tg_host = k2hr3apiutil_1.default.getSafeString(req.query.host);
if (k2hr3apiutil_1.default.isIpAddressString(tg_host)) {
ip = tg_host.toLowerCase();
}
else {
hostname = tg_host.toLowerCase();
}
}
else {
// get ip address
ip = k2hr3apiutil_1.default.getClientIpAddre