UNPKG

k2hr3-api

Version:

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

1,479 lines (1,359 loc) 90.3 kB
/* * 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: * */ 'use strict'; var express = require('express'); var router = express.Router(); var r3token = require('../lib/k2hr3tokens'); var apiutil = require('../lib/k2hr3apiutil'); var resutil = require('../lib/k2hr3resutil'); var r3userdata = require('../lib/k2hr3userdata'); var k2hr3 = require('../lib/k2hr3dkc'); var r3keys = require('../lib/k2hr3keys').getK2hr3Keys; // Debug logging objects var r3logger = require('../lib/dbglogging'); //--------------------------------------------------------- // Configuration // * Get role full path which is allowed to remove ip address // * Get expiration for role tokens //--------------------------------------------------------- var delhost_role_yrn = null; var expire_rtoken = 0; var expire_reg_rtoken = 0; (function() { var r3Conf = require('../lib/k2hr3config').r3ApiConfig; var apiConf = new r3Conf(); var admincfgobj = apiConf.getK2hr3AdminConfig(); if(apiutil.isSafeEntity(admincfgobj) && apiutil.isSafeString(admincfgobj.tenant) && apiutil.isSafeString(admincfgobj.delhostrole)){ var keys = r3keys(null, admincfgobj.tenant.trim()); delhost_role_yrn= keys.ROLE_TOP_KEY + ':' + admincfgobj.delhostrole.trim(); }else{ r3logger.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('/', function(req, res, next) { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.baseUrl) ) { result = { result: false, message: 'POST request or url or query is wrong' }; r3logger.elog(result.message); resutil.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 var keys = r3keys(); var requestptn = new RegExp(keys.MATCH_URI_GET_ROLE_DATA); // regex = /^\/v1\/role\/(.*)/ var reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(apiutil.isEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === apiutil.getSafeString(reqmatchs[1])){ result = { result: false, message: 'POST request url does not have role name' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // role name var name = reqmatchs[1]; name = name.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('/', function(req, res, next) { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.baseUrl) ) { result = { result: false, message: 'PUT request or url or query is wrong' }; r3logger.elog(result.message); resutil.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 var keys = r3keys(); var requestptn = new RegExp(keys.MATCH_URI_GET_ROLE_DATA); // regex = /^\/v1\/role\/(.*)/ var reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(apiutil.isEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === apiutil.getSafeString(reqmatchs[1])){ result = { result: false, message: 'POST request url does not have role name' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // role name var name = reqmatchs[1]; name = name.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. // function postRole(req, res, next) // eslint-disable-line no-unused-vars { var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.body) || !apiutil.isSafeEntity(req.body.role) ) { result = { result: false, message: 'POST body does not have role data' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ var token_result = r3token.checkToken(req, true, true); // scoped, user token if(!token_result.result){ r3logger.elog(token_result.message); var _status = token_result.status; delete token_result.status; resutil.errResponse(req, res, _status, token_result); return; } var token_info = token_result.token_info; //------------------------------ // check arguments //------------------------------ // name if(!apiutil.isSafeString(req.body.role.name)){ result = { result: false, message: 'role:name field is wrong : ' + JSON.stringify(req.body.role.name) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var keys = r3keys(token_info.user, token_info.tenant); var name = apiutil.getSafeString(req.body.role.name); name = name.toLowerCase(); // role name is only name or full yrn path var nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/ var namematchs = name.match(nameptn); if(!apiutil.isEmptyArray(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)){ r3logger.elog('POST request query has wrong yrn full path to role'); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // policies var policiesptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ var policiespram= apiutil.getNormalizeParameter(req.body.role.policies, policiesptn, null); if(false === policiespram.result){ result = { result: false, message: 'role:policies field is wrong : ' + JSON.stringify(req.body.role.policies) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var policies = policiespram.parameter; // alias var aliasptn = new RegExp('^' + keys.MATCH_ANY_TENANT_ROLE); // regex = /^yrn:yahoo:(.*)::(.*):role:(.*)/ var aliaspram = apiutil.getNormalizeParameter(req.body.role.alias, aliasptn, null); if(false === aliaspram.result){ result = { result: false, message: 'role:alias field is wrong : ' + JSON.stringify(req.body.role.alias) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var aliases = aliaspram.parameter; //------------------------------ // set all field to role //------------------------------ result = k2hr3.setRoleAll(token_info.user, token_info.tenant, name, policies, aliases, null, false, null, false); if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'Could not get response from setRoleAll' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'Could not get error message in response from setRoleAll'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } r3logger.dlog('succeed : ' + result.message); res.status(201); // 201: Created res.send(JSON.stringify(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. // function putRole(req, res, next) // eslint-disable-line no-unused-vars { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.query) ) { result = { result: false, message: 'PUT argument does not have any data' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ var token_result = r3token.checkToken(req, true, true); // scoped, user token if(!token_result.result){ r3logger.elog(token_result.message); var _status = token_result.status; delete token_result.status; resutil.errResponse(req, res, _status, token_result); return; } var token_info = token_result.token_info; //------------------------------ // check arguments //------------------------------ // name if(!apiutil.isSafeString(req.query.name)){ result = { result: false, message: 'role:name field is wrong : ' + JSON.stringify(req.query.name) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var keys = r3keys(token_info.user, token_info.tenant); var name = apiutil.getSafeString(req.query.name); name = name.toLowerCase(); // role name is only name or full yrn path var nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/ var namematchs = name.match(nameptn); if(!apiutil.isEmptyArray(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)){ result = { result: false, message: 'PUT request query has wrong yrn full path to role' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // policies var policies = null; if('' === req.query.policies){ policies = ''; }else if(apiutil.isSafeString(req.query.policies)){ // policies is encoded by JSON, this value is array. // var tmppolicies = apiutil.getSafeString(req.query.policies); if(apiutil.checkSimpleJSON(tmppolicies)){ tmppolicies = JSON.parse(tmppolicies); } var policiesptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ var policiespram= apiutil.getNormalizeParameter(tmppolicies, policiesptn, null); if(false === policiespram.result){ result = { result: false, message: 'role:policies field is wrong : ' + req.query.policies }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } policies = policiespram.parameter; } // alias var aliases = null; if('' === req.query.alias){ aliases = ''; }else if(apiutil.isSafeString(req.query.alias)){ // alias is encoded by JSON, this value is array. // var tmpaliases = apiutil.getSafeString(req.query.alias); if(apiutil.checkSimpleJSON(tmpaliases)){ tmpaliases = JSON.parse(tmpaliases); } var aliasptn = new RegExp('^' + keys.MATCH_ANY_TENANT_ROLE); // regex = /^yrn:yahoo:(.*)::(.*):role:(.*)/ var aliaspram = apiutil.getNormalizeParameter(tmpaliases, aliasptn, null); if(false === aliaspram.result){ result = { result: false, message: 'role:alias field is wrong : ' + req.query.alias }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } aliases = aliaspram.parameter; } //------------------------------ // set all field to role //------------------------------ result = k2hr3.setRoleAll(token_info.user, token_info.tenant, name, policies, aliases, null, false, null, false); if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'Could not get response from setRoleAll' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'Could not get error message in response from setRoleAll'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } r3logger.dlog('succeed : ' + result.message); res.status(201); // 201: Created res.send(JSON.stringify(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). // function postRoleHost(role, req, res, next) // eslint-disable-line no-unused-vars { var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.body) || !apiutil.isSafeEntity(req.body.host) ) { result = { result: false, message: 'POST body does not have host data' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ var token_result = r3token.checkToken(req, true); // scoped, both token if(!token_result.result){ r3logger.elog(token_result.message); var _status = token_result.status; delete token_result.status; resutil.errResponse(req, res, _status, token_result); return; } var is_host_req = (!apiutil.isArray(req.body.host) && !apiutil.isSafeString(req.body.host.host)); var token_info = token_result.token_info; var keys = r3keys(token_info.user, token_info.tenant); //------------------------------ // check arguments //------------------------------ // role name check var name = apiutil.getSafeString(role); name = name.toLowerCase(); var nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/ var namematchs = name.match(nameptn); if(!apiutil.isEmptyArray(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)){ result = { result: false, message: 'POST request url has wrong yrn full path to role' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } //------------------------------ // build parameters //------------------------------ var port; var cuk; var extra; var tag; var host_info; if(!is_host_req){ // // request from user token // var hostArray; if(apiutil.isArray(req.body.host)){ hostArray = req.body.host; }else{ hostArray = [ req.body.host ]; } // check array and make ip array var hostnameArray = []; var ipArray = []; for(var cnt = 0; cnt < hostArray.length; ++cnt){ if(!apiutil.isSafeString(hostArray[cnt].host)){ result = { result: false, message: 'host is not specified.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // hostname or ip address var tg_host = apiutil.getSafeString(hostArray[cnt].host); var tg_ip = null; if(apiutil.isIpAddressString(tg_host)){ tg_ip = tg_host.toLowerCase(); tg_host = null; }else{ tg_host = tg_host.toLowerCase(); tg_ip = null; } // port port = 0; // default any if(apiutil.isSafeEntity(hostArray[cnt].port)){ if(isNaN(hostArray[cnt].port)){ result = { result: false, message: 'POST request has port which is not number: ' + JSON.stringify(hostArray[cnt].port) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } port = parseInt(hostArray[cnt].port); } // cuk cuk = null; // default any if(apiutil.isSafeString(hostArray[cnt].cuk) && apiutil.isSafeString(hostArray[cnt].cuk.trim())){ cuk = apiutil.getSafeString(hostArray[cnt].cuk).trim(); } // extra extra = null; if(apiutil.isSafeString(hostArray[cnt].extra)){ extra = apiutil.getSafeString(hostArray[cnt].extra); } // tag tag = null; if(apiutil.isSafeString(hostArray[cnt].tag)){ tag = apiutil.getSafeString(hostArray[cnt].tag); } // set base host information if(null !== tg_host){ host_info = { ip: null, hostname: tg_host, port: port, cuk: cuk, extra: extra, tag: tag }; }else{ // null !== tg_ip host_info = { ip: tg_ip, hostname: null, port: port, cuk: cuk, extra: extra, tag: tag }; } // set optional keys if(apiutil.isSafeString(hostArray[cnt].inboundip)){ if(!apiutil.isIpAddressString(hostArray[cnt].inboundip)){ result = { result: false, message: 'POST request has inbound ip address which is not ignore ip address string: ' + JSON.stringify(hostArray[cnt].inboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } host_info.inboundip = apiutil.getSafeString(hostArray[cnt].inboundip); } if(apiutil.isSafeString(hostArray[cnt].outboundip)){ if(!apiutil.isIpAddressString(hostArray[cnt].outboundip)){ result = { result: false, message: 'POST request has outbound ip address which is not ignore ip address string: ' + JSON.stringify(hostArray[cnt].outboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } host_info.outboundip = apiutil.getSafeString(hostArray[cnt].outboundip); } // push array if(null !== tg_host){ hostnameArray.push(host_info); }else{ // null !== tg_ip ipArray.push(host_info); } } if(apiutil.isEmptyArray(hostnameArray)){ hostnameArray = null; } if(apiutil.isEmptyArray(ipArray)){ ipArray = null; } var clear_hostname = false; var clear_ips = false; if(apiutil.isSafeEntity(req.body.clear_hostname) && 'boolean' === typeof req.body.clear_hostname){ clear_hostname = req.body.clear_hostname; } if(apiutil.isSafeEntity(req.body.clear_ips) && 'boolean' === typeof req.body.clear_ips){ clear_ips = req.body.clear_ips; } // // Add hostnames and ips ---> Need User Token // result = k2hr3.updateRoleHosts(token_info.user, token_info.tenant, name, hostnameArray, clear_hostname, ipArray, clear_ips); }else{ // // request from host(token) // // get ip address var ip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(ip)){ result = { result: false, message: 'Could not get ip address from request.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // port port = 0; // default any if(apiutil.isSafeEntity(req.body.host.port)){ if(isNaN(req.body.host.port)){ result = { result: false, message: 'POST request has port which is not number: ' + JSON.stringify(req.body.host.port) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } port = parseInt(req.body.host.port); } // cuk cuk = null; // default any if(apiutil.isSafeString(req.body.host.cuk) && apiutil.isSafeString(req.body.host.cuk.trim())){ cuk = apiutil.getSafeString(req.body.host.cuk).trim(); } // extra extra = null; if(apiutil.isSafeString(req.body.host.extra)){ extra = apiutil.getSafeString(req.body.host.extra); if(apiutil.checkSimpleJSON(extra)){ extra = JSON.parse(extra); } } // tag tag = null; if(apiutil.isSafeString(req.body.host.tag)){ tag = apiutil.getSafeString(req.body.host.tag); if(apiutil.checkSimpleJSON(tag)){ tag = JSON.parse(tag); } } // inboundip(optional) var inboundip = null; if(apiutil.isSafeString(req.body.host.inboundip)){ if(!apiutil.isIpAddressString(req.body.host.inboundip)){ result = { result: false, message: 'POST request has inbound ip address which is not ignore ip address string: ' + JSON.stringify(req.body.host.inboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } inboundip = apiutil.getSafeString(req.body.host.inboundip); } // outboundip(optional) var outboundip = null; if(apiutil.isSafeString(req.body.host.outboundip)){ if(!apiutil.isIpAddressString(req.body.host.outboundip)){ result = { result: false, message: 'POST request has outbound ip address which is not ignore ip address string: ' + JSON.stringify(req.body.host.outboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } outboundip = apiutil.getSafeString(req.body.host.outboundip); } // // Add ip address ---> Role Token or User Token // result = k2hr3.addHost(token_info.tenant, name, null, ip, port, cuk, extra, tag, inboundip, outboundip); } //------------------------------ // check result //------------------------------ if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'Could not get response from addHost' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'Could not get error message in response from addHost'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } r3logger.dlog('succeed : ' + result.message); res.status(201); // 201: Created res.send(JSON.stringify(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). // function putRoleHost(role, req, res, next) // eslint-disable-line no-unused-vars { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.query) ) { result = { result: false, message: 'PUT argument does not have any data' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ var token_result = r3token.checkToken(req, true); // scoped, both token if(!token_result.result){ r3logger.elog(token_result.message); var _status = token_result.status; delete token_result.status; resutil.errResponse(req, res, _status, token_result); return; } var is_host_req = !apiutil.isSafeString(req.query.host); var token_info = token_result.token_info; var keys = r3keys(token_info.user, token_info.tenant); //------------------------------ // check arguments //------------------------------ // role name check var name = apiutil.getSafeString(role); name = name.toLowerCase(); var nameptn = new RegExp('^' + keys.ROLE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:role:(.*)/ var namematchs = name.match(nameptn); if(!apiutil.isEmptyArray(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)){ result = { result: false, message: 'POST request url has wrong yrn full path to role' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } // hostname var hostname= null; var ip = null; if(!is_host_req){ if(!apiutil.isSafeString(req.query.host)){ result = { result: false, message: 'host is not specified.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var tg_host = apiutil.getSafeString(req.query.host); if(apiutil.isIpAddressString(tg_host)){ ip = tg_host.toLowerCase(); }else{ hostname= tg_host.toLowerCase(); } }else{ // get ip address ip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(ip)){ result = { result: false, message: 'Could not get ip address from request.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } // port var port; if(apiutil.isSafeString(req.query.port)){ if(isNaN(req.query.port)){ result = { result: false, message: 'PUT request has port which is not number: ' + JSON.stringify(req.query.port) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } port = parseInt(req.query.port); }else{ port = 0; // default any } // cuk var cuk; if(apiutil.isSafeString(req.query.cuk) && apiutil.isSafeString(req.query.cuk.trim())){ cuk = apiutil.getSafeString(req.query.cuk).trim(); }else{ cuk = null; } // extra var extra; if(apiutil.isSafeString(req.query.extra)){ extra = apiutil.getSafeString(req.query.extra); if(apiutil.checkSimpleJSON(extra)){ extra = JSON.parse(extra); // extra encoded JSON } }else{ extra = null; } // tag var tag; if(apiutil.isSafeString(req.query.tag)){ tag = apiutil.getSafeString(req.query.tag); if(apiutil.checkSimpleJSON(tag)){ tag = JSON.parse(tag); // tag encoded JSON } }else{ tag = null; } // make base host information var host_info = { ip: ip, hostname: hostname, port: port, cuk: cuk, extra: extra, tag: tag }; // set inboundip(optional) var inboundip = null; if(apiutil.isSafeString(req.query.inboundip)){ if(!apiutil.isIpAddressString(req.query.inboundip)){ result = { result: false, message: 'PUT request has inbound ip address which is not ignore ip address string: ' + JSON.stringify(req.query.inboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } inboundip = apiutil.getSafeString(req.query.inboundip); host_info.inboundip = inboundip; } // set outboundip(optional) var outboundip = null; if(apiutil.isSafeString(req.query.outboundip)){ if(!apiutil.isIpAddressString(req.query.outboundip)){ result = { result: false, message: 'PUT request has outbound ip address which is not ignore ip address string: ' + JSON.stringify(req.query.outboundip) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } outboundip = apiutil.getSafeString(req.query.outboundip); host_info.outboundip= outboundip; } //------------------------------ // add host to role //------------------------------ if(!is_host_req){ // Add hostname ---> Need User Token if(null === ip){ result = k2hr3.updateRoleHosts(token_info.user, token_info.tenant, name, host_info); }else{ result = k2hr3.updateRoleHosts(token_info.user, token_info.tenant, name, null, false, host_info); } }else{ // Add ip address ---> Role Token or User Token result = k2hr3.addHost(token_info.tenant, name, null, ip, port, cuk, extra, tag, inboundip, outboundip); } if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'Could not get response from addHost' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'Could not get error message in response from addHost'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } r3logger.dlog('succeed : ' + result.message); res.status(201); // 201: Created res.send(JSON.stringify(result)); } //--------------------------------------------------------- // Router GET //--------------------------------------------------------- // // Mountpath : '/v1/role/<role{/...}>' // // GET '/v1/role/<role{/...}>' : get role on version 1 // HEADER : X-Auth-Token => User token // URL arguments : expand => "true"(default) or "false" // response : { // "result": true or false // "message": error message // "role": { // policies: array, // aliases: array <--- only not expand // hosts: { <--- only not expand // 'hostnames': [ hostname array or empty array // <hostname> <port> <cuk> <extra> <tag>, (if any port, port is *) // ... // ], // 'ips': [ ip address array or empty array // <ip address> <port> <cuk> <extra> <tag>,(if any port, port is *) // ... // ] // } // } // } // // GET '/v1/role/token/<role{/...}>' : get role token on version 1 // HEADER : X-Auth-Token => User token or Role token // URL arguments : expire => "expire time(unix time value)" or undefined(default 24H) // response : { // "result": true or false // "message": error message // "token": "role token" // "registerpath": "path for registering" // } // // GET '/v1/role/token/list/<role{/...}>': get list of role tokens on version 1 // HEADER : X-Auth-Token => User token // URL arguments : expand => "true"(default) or "false" // response : { // result: true/false // message: null or error message string // tokens: { // "token": { // date: create date(UTC ISO 8601) // expire: expire date(UTC ISO 8601) // user: user name if user created this token // hostname: hostname if this token was created by host(name) // ip: ip address if this token was created by ip // port: port number, if specified port when created token // cuk: cuk, if specified cuk when created token // }, // ... // } // } // or // { // result: true/false // message: null or error message string // tokens: [ // "role token", // .... // ] // } // // This mount point is for creating(update) role or creating(update) host in role. // And get role token by host(ip address) or user(user token), update role token by // role token. // router.get('/', function(req, res, next) { r3logger.dlog('CALL:', req.method, req.url); if('GET' !== req.method){ // HEAD request comes here, so it should be routed to head function. next(); return; } res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.baseUrl) ) { result = { result: false, message: 'GET request or url is wrong' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token for API mode //------------------------------ var token_str = null; var token_type = null; var token_info = null; var keys = r3keys(); if(r3token.hasAuthTokenHeader(req)){ var token_result = r3token.checkToken(req, true); // scoped, both token if(!token_result.result){ r3logger.elog(token_result.message); var _status = token_result.status; delete token_result.status; resutil.errResponse(req, res, _status, token_result); return; } token_str = token_result.token; token_type = token_result.token_type; token_info = token_result.token_info; keys = r3keys(token_info.user, token_info.tenant); } //------------------------------ // get role name //------------------------------ // check get token type and parse role name var is_get_token= false; var is_get_list = false; var requestptn = new RegExp(keys.MATCH_URI_GET_RTOKEN_LIST); // regex = /^\/v1\/role\/token\/list\/(.*)/ var reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(!apiutil.isEmptyArray(reqmatchs) && 2 <= reqmatchs.length && '' !== apiutil.getSafeString(reqmatchs[1])){ // get list of tokens is_get_list = true; }else{ // recheck requestptn = new RegExp(keys.MATCH_URI_GET_RTOKEN); // regex = /^\/v1\/role\/token\/(.*)/ reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(!apiutil.isEmptyArray(reqmatchs) && 2 <= reqmatchs.length && '' !== apiutil.getSafeString(reqmatchs[1])){ // get token is_get_token= true; }else{ // retry parse role name requestptn = new RegExp(keys.MATCH_URI_GET_ROLE_DATA); // regex = /^\/v1\/role\/(.*)/ reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(apiutil.isEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === apiutil.getSafeString(reqmatchs[1])){ result = { result: false, message: 'GET request url does not have role name' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } } // check role name is only name or full yrn path var name = reqmatchs[1]; name = name.toLowerCase(); var nameptn = new RegExp('^' + keys.MATCH_ANY_TENANT_ROLE); // regex = /^yrn:yahoo:(.*)::(.*):role:(.*)/ var namematchs = name.match(nameptn); if(apiutil.isEmptyArray(namematchs) || namematchs.length < 4){ // // name is not full yrn to role, then check wrong role name // nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if(name.match(nameptn)){ result = { result: false, message: 'GET request query has wrong yrn full path to role' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // role name is not full yrn, we need tenant name if(!apiutil.isSafeEntity(keys.ROLE_TOP_KEY)){ result = { result: false, message: 'GET request role name which is not full yrn, and not token. role name must be full yrn, if token is not specified.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // make full yrn for role name name = keys.ROLE_TOP_KEY + ':' + name; }else{ // // name is full yrn to role. // need to check tenant name when token is specified. // if(null !== token_type && (!apiutil.isSafeEntity(token_info) || !apiutil.isSafeString(token_info.tenant) || !apiutil.compareCaseString(namematchs[2], token_info.tenant))){ result = { result: false, message: 'GET request query has wrong tenant yrn full path(tenant=' + namematchs[2] + ') or not specify tenant.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } // Run if(is_get_token){ //------------------------------ // GET ROLE TOKEN //------------------------------ // token_info: null(undefined) => not specify token, put token by host ip address // user token => put token by user // role token => update token by role // getRoleToken(name, token_info, token_type, token_str, req, res); }else if(is_get_list){ //------------------------------ // GET LIST OF ROLE TOKENS //------------------------------ if('user' === apiutil.getSafeString(token_type)){ getListRoleTokens(name, token_info, req, res); }else{ result = { result: false, message: 'GET request without UserToken for getting list of role(' + name + ') tokens, need User Token.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } }else{ //------------------------------ // GET ROLE DATA //------------------------------ if('user' === apiutil.getSafeString(token_type)){ getRole(name, token_info, req, res); }else{ result = { result: false, message: 'GET request without UserToken for getting role(' + name + '), need User Token.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } }); // // Sub router function for GET ROLE DATA // // Mountpath : '/v1/role/<role{/...}>' // // GET '/v1/role/<role{/...}>' : get role on version 1 // HEADER : X-Auth-Token => User token // URL arguments : expand => "true"(default) or "false" // response : { // "result": true or false // "message": error message // "role": { // policies: array, // aliases: array <--- only not expand // hosts: { <--- only not expand // 'hostnames': [ hostname array or empty array // <hostname> <port> <cuk> <extra> <tag>, (if any port, port is *) // ... // ], // 'ips': [ ip address array or empty array // <ip address> <port> <cuk> <extra> <tag>,(if any port, port is *) // ... // ] // } // } // } // // This mount point is for creating(update) role or creating(update) host in role. // function getRole(role, token_info, req, res) { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); var result; if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.query) ) { result = { result: false, message: 'GET request query is wrong' }; r3logger.elo