UNPKG

k2hr3-api

Version:

K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules

1,054 lines 123 kB
"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