UNPKG

k2hr3-api

Version:

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

1,301 lines (1,202 loc) 55.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 k2hr3 = require('../lib/k2hr3dkc'); var r3keys = require('../lib/k2hr3keys').getK2hr3Keys; // Debug logging objects var r3logger = require('../lib/dbglogging'); // // Utility for parsing common input parameters // // This function parse token(user or role or not have this) from HTTP request(req), // and role name/resource name(and yrn), etc. // If request URI has resource name(path), do not specify default_resource_name value. // The other hand, when default_resource_name is specified, the request URI can not // have resource name(path) in it. // // return : { // res_obj: { // result: true/false // message: null or error message // }, // res_code: status code(default 200) // parameters: { // token_type: null or 'user' or 'role' // token_str: token string(if user token or role token) // token_info: null or object(returned from checkToken) // user_name: null or user name(if user token) // tenant_name: null or user name(if user token or role token) // keys: k2hr3keys object // res_yrn: target resource yrn // res_name: target resource name // res_tenant: resource's tenant // res_service: resource's service when resource is full yrn, null when not full yrn // } // } // function rawParseBaseParamRequestAPI(req, is_allow_service, default_resource_name) { var res_obj = { result: true, message: null }; var result = { res_obj: res_obj, res_code: 200 }; var parameters = { token_type: null, token_str: null, token_info: null, user_name: null, tenant_name: null, keys: null, res_yrn: null, res_name: null, res_tenant: null, res_service: null }; // // check token for API mode // if(r3token.hasAuthTokenHeader(req)){ var token_result = r3token.checkToken(req, true); // scoped, both token if(!token_result.result){ result.res_obj.result = token_result.result; result.res_obj.message = token_result.message; result.res_code = token_result.status; r3logger.elog(result.res_obj.message); return result; } parameters.token_str = token_result.token; parameters.token_type = token_result.token_type; parameters.token_info = token_result.token_info; parameters.user_name = apiutil.getSafeString(parameters.token_info.user); parameters.tenant_name = apiutil.getSafeString(parameters.token_info.tenant).toLowerCase(); parameters.keys = r3keys(parameters.token_info.user, parameters.token_info.tenant); }else{ parameters.keys = r3keys(); } // // check service parameter in request // var service_param = null; if(is_allow_service){ if(apiutil.compareCaseString('POST', req.method)){ if(apiutil.isSafeEntity(req.body) && apiutil.isSafeString(req.body.service)){ service_param = apiutil.getSafeString(req.body.service).trim(); } }else{ if(apiutil.isSafeEntity(req.query) && apiutil.isSafeString(req.query.service)){ service_param = apiutil.getSafeString(req.query.service).trim(); } } } // // get resource full yrn // var requestptn = new RegExp('^/v1/resource/(.*)'); // regex = /^\/v1\/resource\/(.*)/ var reqmatchs = decodeURI(req.baseUrl).match(requestptn); if(apiutil.isEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === apiutil.getSafeString(reqmatchs[1])){ if(!apiutil.isSafeString(default_resource_name)){ result.res_obj.result = false; result.res_obj.message = 'Default resource name is not specified or wrong value : ' + JSON.stringify(default_resource_name); result.res_code = 400; // 400: Bad Request r3logger.elog(result.res_obj.message); return result; } parameters.res_yrn = apiutil.getSafeString(default_resource_name); }else{ parameters.res_yrn = reqmatchs[1]; } parameters.res_yrn = parameters.res_yrn.toLowerCase(); // // make resource name from resource yrn // var nameptn = new RegExp('^' + parameters.keys.MATCH_ANY_TENANT_RESOURCE); // regex = /^yrn:yahoo:(.*)::(.*):resource:(.*)/ var namematchs = parameters.res_yrn.match(nameptn); if(apiutil.isEmptyArray(namematchs) || namematchs.length < 4){ // res_yrn is not full yrn to resource, then check wrong resource name nameptn = new RegExp('^' + parameters.keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if(parameters.res_yrn.match(nameptn)){ result.res_obj.result = false; result.res_obj.message = 'Request query has wrong yrn full path to resource'; result.res_code = 400; // 400: Bad Request r3logger.elog(result.res_obj.message); return result; } // no token need full yrn to resource(other token has tenant name) if(null === parameters.token_type){ result.res_obj.result = false; result.res_obj.message = 'Request query does not have yrn full path to resource'; result.res_code = 400; // 400: Bad Request r3logger.elog(result.res_obj.message); return result; } // make resource yrn from resource name(sometimes, a case of user token come here.) parameters.res_name = parameters.res_yrn; parameters.res_tenant = parameters.tenant_name; // resource is only name, then resource's tenant is same. parameters.res_service = apiutil.isSafeString(service_param) ? service_param.toLowerCase() : null; parameters.keys = r3keys(parameters.token_info.user, parameters.res_tenant, parameters.res_service); parameters.res_yrn = parameters.keys.RESOURCE_TOP_KEY + ':' + parameters.res_name; }else{ // res_yrn is full yrn to resource, then need to check tenant name if(null !== parameters.token_type && !apiutil.compareCaseString(namematchs[2], apiutil.getSafeString(parameters.token_info.tenant))){ result.res_obj.result = false; result.res_obj.message = 'Request query has wrong yrn full path(tenant=' + namematchs[2] + ') to resource(tenant=' + apiutil.getSafeString(parameters.token_info.tenant) + ')'; result.res_code = 400; // 400: Bad Request r3logger.elog(result.res_obj.message); return result; } // check service name if(apiutil.isSafeString(service_param) && !apiutil.compareCaseString(service_param, namematchs[1])){ result.res_obj.result = false; result.res_obj.message = 'Request query has service name(' + service_param + ') and path has service name(' + namematchs[1] + '), but both are not same service name.'; result.res_code = 400; // 400: Bad Request r3logger.elog(result.res_obj.message); return result; } parameters.res_name = namematchs[3].toLowerCase(); parameters.res_tenant = namematchs[2].toLowerCase(); // resource is yrn, then resource's tenant is set from yrn. parameters.res_service = namematchs[1].toLowerCase(); // resource is not yrn, then service is not specified. parameters.keys = r3keys((apiutil.isSafeEntity(parameters.token_info) && apiutil.isSafeString(parameters.token_info.user) ? parameters.token_info.user : null), (apiutil.isSafeEntity(parameters.token_info) && apiutil.isSafeString(parameters.token_info.tenant) ? parameters.token_info.tenant : null), parameters.res_service); } // no error result.parameters = parameters; return result; } // // Mountpath : '/v1/resource' // // POST '/v1/resource' : post resource on version 1 // HEADER : X-Auth-Token => User token // body : { // "resource": { // "name": <resource name> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>" // <resource> can include '/' for hierarchical path // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // data type must be string or json. // if data is null or not specified, this value is not used. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be encoded by encodeURI, because data is allowed CR, control code etc. // but nodejs is decodeURI automatically // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc) // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or string(JSON), this member is set into "keys". // "alias": [<resource yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/@" // specify another resource as "yrn:yahoo:<service>::<tenant>:resource:<resource>" // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or zero array, this member in resource is set empty array. // } // } // response body : { // result: true/false // message: messages // } // // POST '/v1/resource/name' : post resource on version 1 // HEADER : X-Auth-Token => Role token // body : { // "resource": { // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // data type must be string or json. // if data is null or not specified, this value is not used. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be encoded by encodeURI, because data is allowed CR, control code etc. // but nodejs is decodeURI automatically // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc) // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or string(JSON), this member is set into "keys". // } // } // response body : { // result: true/false // message: messages // } // // // POST '/v1/resource/name' : post resource on version 1 => name is full yrn to resource // HEADER : X-Auth-Token => undefined // body : { // "resource": { // "port": <port number> => undefined(null) is allowed. if empty value, default port is 0(any) // "cuk": <container unique key> => undefined(null) is allowed. if empty value, any value. // "role": <role full yrn> => key is "yrn:yahoo:<service>::<tenant>:role:<role>{/<role>...}" // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // data type must be string or json. // if data is null or not specified, this value is not used. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be encoded by encodeURI, because data is allowed CR, control code etc. // but nodejs is decodeURI automatically // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc) // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or string(JSON), this member is set into "keys". // } // } // response body : { // result: true/false // message: messages // } // // router.post('/', function(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.body) || !apiutil.isSafeEntity(req.body.resource) ) { result = { result: false, message: 'POST body does not have resource data' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check common parameters(token, role, resource etc) //------------------------------ result = rawParseBaseParamRequestAPI(req, false, apiutil.isSafeEntity(req.body.resource.name) ? req.body.resource.name : null); if(!result.res_obj.result){ r3logger.elog(result.res_obj.message); resutil.errResponse(req, res, result.res_code, result.res_obj); return; } var comparam = result.parameters; //------------------------------ // check resource //------------------------------ if(null !== comparam.token_type && (comparam.tenant_name !== comparam.res_tenant || apiutil.isSafeString(comparam.res_service))){ r3logger.elog('POST request resource(' + JSON.stringify(comparam.res_name) + ') is under tenant(' + JSON.stringify(comparam.res_tenant) + ') and service(' + JSON.stringify(comparam.res_service) + '), it is not under tenant(' + JSON.stringify(comparam.tenant_name) + ').'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // data type var type = null; if(apiutil.isSafeString(req.body.resource.type)){ type = apiutil.getSafeString(req.body.resource.type); if(!apiutil.compareCaseString('string', type) && !apiutil.compareCaseString('object', type)){ result = { result: false, message: 'POST resource:type field is wrong : ' + JSON.stringify(req.body.resource.type) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } }else{ type = comparam.keys.VALUE_STRING_TYPE; // default type is string } // data var data = null; if(apiutil.compareCaseString(comparam.keys.VALUE_STRING_TYPE, type)){ if(apiutil.isSafeEntity(req.body.resource.data) && '' === req.body.resource.data){ data = ''; }else if(apiutil.isSafeString(req.body.resource.data)){ // data is string(nodejs is decodeURI automatically) // this value includes control codes(\n, etc) data = apiutil.getSafeString(req.body.resource.data); } }else{ // type == object if(apiutil.isSafeEntity(req.body.resource.data)){ data = req.body.resource.data; } } // keys var resource_keys = null; if(apiutil.isSafeEntity(req.body.resource.keys)){ if('' === req.body.resource.keys){ resource_keys = ''; }else if(req.body.resource.keys instanceof Object){ resource_keys = req.body.resource.keys; }else if(apiutil.isSafeString(req.body.resource.keys)){ resource_keys = req.body.resource.keys; if(apiutil.checkSimpleJSON(resource_keys)){ resource_keys = JSON.parse(resource_keys); } }else{ result = { result: false, message: 'POST resource:keys field is wrong : ' + JSON.stringify(req.body.resource.keys) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } // alias if('user' !== comparam.token_type && apiutil.isSafeEntity(req.body.resource.alias)){ result = { result: false, message: 'POST resource:alias field is specified, but it is not allowed by not user token : ' + JSON.stringify(req.body.resource.alias) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var aliases = null; if('user' === comparam.token_type){ var aliasptn = new RegExp('^' + comparam.keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ var aliaspram = apiutil.getNormalizeParameter(req.body.resource.alias, aliasptn, null); if(false === aliaspram.result){ result = { result: false, message: 'POST resource:alias field is wrong : ' + JSON.stringify(req.body.resource.alias) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } aliases = aliaspram.parameter; } // role yrn/ip address/port for no token var clientip = null; var port = 0; var cuk = null; var role_yrn = null; if(null !== comparam.token_type && (apiutil.isSafeEntity(req.body.resource.port) || apiutil.isSafeEntity(req.body.resource.cuk) || apiutil.isSafeEntity(req.body.resource.role))){ result = { result: false, message: 'POST resource:port/cuk/role field is specified, but it is not allowed by no token : port=' + JSON.stringify(req.body.resource.port) + ', cuk=' + JSON.stringify(req.body.resource.cuk) + ', role=' + JSON.stringify(req.body.resource.role) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } if(null === comparam.token_type){ // role if(!apiutil.isSafeString(req.body.resource.role)){ result = { result: false, message: 'POST request does not have role yrn in post data.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = apiutil.getSafeString(req.body.resource.role); // ip clientip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(clientip)){ result = { result: false, message: 'POST request does not have ip address for client.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // port if(apiutil.isSafeString(req.body.resource.port) && !isNaN(req.body.resource.port)){ port = parseInt(req.body.resource.port); }else{ port = 0; } // cuk if(apiutil.isSafeString(req.body.resource.cuk) && apiutil.isSafeString(req.body.resource.cuk.trim())){ cuk = req.body.resource.cuk.trim(); } } //------------------------------ // set all field to resource //------------------------------ if('user' === comparam.token_type){ result = k2hr3.setResourceAll(comparam.user_name, comparam.tenant_name, comparam.res_name, type, data, resource_keys, aliases); }else if('role' === comparam.token_type){ result = k2hr3.setResourceAllByRole(comparam.token_info.role, comparam.tenant_name, comparam.res_name, type, data, resource_keys); }else if(null === comparam.token_type){ result = k2hr3.setResourceAllByIP(clientip, port, cuk, role_yrn, comparam.res_name, type, data, resource_keys); }else{ // broken token result = { result: false, message: 'POST request is failure by internal error(token data broken).' }; r3logger.elog(result.message); resutil.errResponse(req, res, 500, result); // 500: Internal Error return; } if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'POST Could not get response from setResourceAll' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'POST Could not get error message in response from setResourceAll'; } } 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)); }); // // Mountpath : '/v1/resource' // // PUT '/v1/resource' : post resource on version 1 // HEADER : X-Auth-Token => User token // url argument : // "name": <resource name> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>" // <resource> can include '/' for hierarchical path // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // type is object or string, default is string. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be formatted by JSON, and it is allowed CR, control code etc. // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc), it is formatted by JSON. // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or associative array, this member is set into "keys". // "alias": [<resource yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/@" // specify another resource array as "yrn:yahoo:<service>::<tenant>:resource:<resource>", it is formatted by JSON. // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or zero array, this member in resource is set empty array. // response body : { // result: true/false // message: messages // } // // PUT '/v1/resource/name' : post resource on version 1 // HEADER : X-Auth-Token => Role token // url argument : // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // type is object or string, default is string. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be formatted by JSON, and it is allowed CR, control code etc. // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc), it is formatted by JSON. // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or associative array, this member is set into "keys". // response body : { // result: true/false // message: messages // } // // PUT '/v1/resource/name' : post resource on version 1 => name is full yrn to resource // HEADER : X-Auth-Token => undefined // url argument : // "port": <port number> => undefined(null) is allowed. if empty value, default port is 0(any) // "cuk": <container unique key> => undefined(null) is allowed. if empty value, any value. // "role": <role full yrn> => key is "yrn:yahoo:<service>::<tenant>:role:<role>{/<role>...}" // "type": <data type> => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/type" // type is object or string, default is string. // "data": <resource data> => value for "yrn:yahoo:<service>::<tenant>:resource:<resource>" // data must be formatted by JSON, and it is allowed CR, control code etc. // "keys": {foo: bar, ...} => key is "yrn:yahoo:<service>::<tenant>:resource:<resource>/keys" // specify any associative array(SSL certificate, host key, etc), it is formatted by JSON. // if null or undefined is specified, not update this member in resource when this resource exists. // if '' or associative array, this member is set into "keys". // response body : { // result: true/false // message: messages // } // router.put('/', function(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 common parameters(token, role, resource etc) //------------------------------ result = rawParseBaseParamRequestAPI(req, false, apiutil.isSafeEntity(req.query.name) ? req.query.name : null); if(!result.res_obj.result){ r3logger.elog(result.res_obj.message); resutil.errResponse(req, res, result.res_code, result.res_obj); return; } var comparam = result.parameters; //------------------------------ // check resource //------------------------------ if(null !== comparam.token_type && (comparam.tenant_name !== comparam.res_tenant || apiutil.isSafeString(comparam.res_service))){ r3logger.elog('PUT request resource(' + JSON.stringify(comparam.res_name) + ') is under tenant(' + JSON.stringify(comparam.res_tenant) + ') and service(' + JSON.stringify(comparam.res_service) + '), it is not under tenant(' + JSON.stringify(comparam.tenant_name) + ').'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // data type var type = null; if(apiutil.isSafeString(req.query.type)){ type = apiutil.getSafeString(req.query.type); if(!apiutil.compareCaseString('string', type) && !apiutil.compareCaseString('object', type)){ result = { result: false, message: 'PUT resource:type field is wrong : ' + JSON.stringify(req.query.type) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } }else{ type = comparam.keys.VALUE_STRING_TYPE; // default type is string } // data var data = null; if(apiutil.compareCaseString(comparam.keys.VALUE_STRING_TYPE, type)){ if(apiutil.isSafeEntity(req.query.data) && '' === req.query.data){ data = ''; }else if(apiutil.isSafeString(req.query.data)){ data = apiutil.getSafeString(req.query.data); if(apiutil.checkSimpleJSON(req.query.data)){ data = JSON.parse(req.query.data); // decode JSON } } }else{ // type == object if(apiutil.isSafeString(req.query.data)){ data = apiutil.getSafeString(req.query.data); if(apiutil.checkSimpleJSON(req.query.data)){ data = JSON.parse(req.query.data); // decode JSON } } } // keys var resource_keys = null; if(apiutil.isSafeEntity(req.query.keys) && '' === req.query.keys){ resource_keys = ''; }else if(apiutil.isSafeString(req.query.keys)){ // keys is encoded by JSON, this value is associative array. // resource_keys = apiutil.getSafeString(req.query.keys); if(apiutil.checkSimpleJSON(resource_keys)){ resource_keys = JSON.parse(resource_keys); } } // alias if('user' !== comparam.token_type && apiutil.isSafeEntity(req.query.alias)){ result = { result: false, message: 'PUT resource:alias field is specified, but it is not allowed by not user token : ' + JSON.stringify(req.query.alias) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } var aliases = null; if('user' === comparam.token_type){ if(apiutil.isSafeEntity(req.query.alias) && '' === 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('^' + comparam.keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ var aliaspram = apiutil.getNormalizeParameter(tmpaliases, aliasptn, null); if(false === aliaspram.result){ result = { result: false, message: 'PUT resource:alias field is wrong : ' + req.query.alias }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } aliases = aliaspram.parameter; } } // role yrn/ip address/port for no token var clientip = null; var port = 0; var cuk = null; var role_yrn = null; if(null !== comparam.token_type && (apiutil.isSafeEntity(req.query.port) || apiutil.isSafeEntity(req.query.cuk) || apiutil.isSafeEntity(req.query.role))){ result = { result: false, message: 'PUT resource:port/cuk/role field is specified, but it is not allowed by no token : port=' + JSON.stringify(req.query.port) + ', cuk=' + JSON.stringify(req.query.cuk) + ', role=' + JSON.stringify(req.query.role) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } if(null === comparam.token_type){ // role if(!apiutil.isSafeString(req.query.role)){ result = { result: false, message: 'PUT request does not have role yrn in post data.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = apiutil.getSafeString(req.query.role); // ip clientip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(clientip)){ result = { result: false, message: 'PUT request does not have ip address for client.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // port if(apiutil.isSafeString(req.query.port) && !isNaN(req.query.port)){ port = parseInt(req.query.port); }else{ port = 0; } // cuk if(apiutil.isSafeString(req.query.cuk) && apiutil.isSafeString(req.query.cuk.trim())){ cuk = req.query.cuk.trim(); } } //------------------------------ // set all field to resource //------------------------------ if('user' === comparam.token_type){ result = k2hr3.setResourceAll(comparam.user_name, comparam.tenant_name, comparam.res_name, type, data, resource_keys, aliases); }else if('role' === comparam.token_type){ result = k2hr3.setResourceAllByRole(comparam.token_info.role, comparam.tenant_name, comparam.res_name, type, data, resource_keys); }else if(null === comparam.token_type){ result = k2hr3.setResourceAllByIP(clientip, port, cuk, role_yrn, comparam.res_name, type, data, resource_keys); }else{ // broken token result = { result: false, message: 'PUT request is failure by internal error(token data broken).' }; r3logger.elog(result.message); resutil.errResponse(req, res, 500, result); // 500: Internal Error return; } if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'PUT Could not get response from setResourceAll' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'PUT Could not get error message in response from setResourceAll'; } } 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)); }); // // Mountpath : '/v1/resource/*' // // GET '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = User token // URL arguments : expand = "true"(default) or "false" // service = service name(optional) // result : { // "result": true or false // "message": error message // "resource": { // string: "string", // object: object // keys: object // aliases: array <--- only not expand // } // } // // GET '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = Role token // URL arguments : type = "string"(default) or "object" or "keys" // keyname = key name(if type is "keys") // service = service name(optional) // result : { // "result": true or false // "message": error message // "resource": string or object // } // // GET '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = undefined // URL arguments : port = port number(undefined is default 0(any)) // cuk = container unique key(empty value, any value) // role = role full yrn // type = "string"(default) or "object" or "keys" // keyname = key name(if type is "keys") // service = service name(optional) // result : { // "result": true or false // "message": error message // "resource": string or object // } // // [NOTE] // The name in '/v1/resource/name' path is allowed resource name or resource full yrn path. // If the name is not yrn path, resource path created by including tenant and service which // are specified in role. // 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 common parameters(token, role, resource etc) //------------------------------ result = rawParseBaseParamRequestAPI(req, true, null); if(!result.res_obj.result){ r3logger.elog(result.res_obj.message); resutil.errResponse(req, res, result.res_code, result.res_obj); return; } var comparam = result.parameters; //------------------------------ // check arguments //------------------------------ // expand type(only user token type) var is_expand = true; if(apiutil.isSafeEntity(req.query) && apiutil.isSafeString(req.query.expand)){ if('user' === comparam.token_type){ if(apiutil.compareCaseString(comparam.keys.VALUE_TRUE, req.query.expand)){ is_expand = true; }else if(apiutil.compareCaseString(comparam.keys.VALUE_FALSE, req.query.expand)){ is_expand = false; }else{ result = { result: false, message: 'GET expand url argument parameter(' + JSON.stringify(req.query.expand) + ') is wrong, it must be ' + comparam.keys.VALUE_TRUE + ' or ' + comparam.keys.VALUE_FALSE + '.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } }else{ r3logger.wlog('GET found unnessesary expand(' + JSON.stringify(req.query.expand) + ') parameter, skip this.'); } } // type, key parameter(role token/no token type) var restype = null; var reskeyname = null; if(null === comparam.token_type || 'role' === comparam.token_type){ if(!apiutil.isSafeEntity(req.query) || !apiutil.isSafeString(req.query.type)){ restype = comparam.keys.VALUE_STRING_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_STRING_TYPE, req.query.type)){ restype = comparam.keys.VALUE_STRING_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_OBJECT_TYPE, req.query.type)){ restype = comparam.keys.VALUE_OBJECT_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_KEYS_TYPE, req.query.type)){ restype = comparam.keys.VALUE_KEYS_TYPE; // key name if(!apiutil.isSafeEntity(req.query) || !apiutil.isSafeString(req.query.keyname)){ result = { result: false, message: 'GET request type=keys, but keyname(' + req.query.keyname + ') parameter is empty.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } reskeyname = req.query.keyname; }else{ result = { result: false, message: 'GET request type(' + req.query.type + ') parameter is wrong.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } } // role yrn/ip address/port for no token var clientip = null; var port = 0; var cuk = null; var role_yrn = null; if(null !== comparam.token_type && (apiutil.isSafeEntity(req.query.port) || apiutil.isSafeEntity(req.query.cuk) || apiutil.isSafeEntity(req.query.role))){ result = { result: false, message: 'GET resource:port/cuk/role field is specified, but it is not allowed by no token : port=' + JSON.stringify(req.query.port) + ', cuk=' + JSON.stringify(req.query.cuk) + ', role=' + JSON.stringify(req.query.role) }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } if(null === comparam.token_type){ // role if(!apiutil.isSafeString(req.query.role)){ result = { result: false, message: 'GET request does not have role yrn in post data.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = apiutil.getSafeString(req.query.role); // ip clientip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(clientip)){ result = { result: false, message: 'GET request does not have ip address for client.' }; r3logger.elog(result.message); resutil.errResponse(req, res, 400, result); // 400: Bad Request return; } // port if(apiutil.isSafeString(req.query.port) && !isNaN(req.query.port)){ port = parseInt(req.query.port); }else{ port = 0; } // cuk if(apiutil.isSafeString(req.query.cuk) && apiutil.isSafeString(req.query.cuk.trim())){ cuk = req.query.cuk.trim(); } } //------------------------------ // Run //------------------------------ if('user' === comparam.token_type){ result = k2hr3.getResource(comparam.user_name, comparam.tenant_name, comparam.res_service, comparam.res_name, is_expand); }else if('role' === comparam.token_type){ // [NOTE] // comparam.token_info.role is role full yrn path, it does not include service name. // Because we do not put role token for role under service path. // result = k2hr3.getResourceByRole(comparam.token_info.role, comparam.res_yrn, restype, reskeyname); }else if(null === comparam.token_type){ // [NOTE] // role_yrn allows a path containing service. // However, the case is rare and should not be used. // result = k2hr3.getResourceByIP(clientip, port, cuk, role_yrn, comparam.res_yrn, restype, reskeyname); }else{ // broken token result = { result: false, message: 'GET request is failure by internal error(token data broken).' }; r3logger.elog(result.message); resutil.errResponse(req, res, 500, result); // 500: Internal Error return; } if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'GET Could not get response from getResource' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'GET Could not get error message in response from getResource'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 404, result); // 404: Not Found return; } r3logger.dlog('succeed : ' + result.message); res.status(200); // 200: OK res.send(JSON.stringify(result)); }); // // Mountpath : '/v1/resource/*' // // HEAD '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = User token // URL arguments : type = "string"(default) or "object" or "keys" // keyname = key name(if type is "keys") // service = service name(if resource is under service) // // HEAD '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = Role token // URL arguments : type = "string"(default) or "object" or "keys" // keyname = key name(if type is "keys") // service = service name(optional) // // HEAD '/v1/resource/name' : get resource on version 1(name is allowed full yrn path) // HEADER : X-Auth-Token = undefined // URL arguments : port = port number(undefined is default 0(any)) // cuk = container unique key(empty value, any value) // role = role full yrn // type = "string"(default) or "object" or "keys" // keyname = key name(if type is "keys") // service = service name(optional) // router.head('/', function(req, res, next) { r3logger.dlog('CALL:', req.method, req.url); if('HEAD' !== req.method){ // If other method request comes here, so it should be routed another function. next(); return; } res.type('application/json; charset=utf-8'); if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.baseUrl) ) { r3logger.elog('HEAD request or url or query is wrong'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check common parameters(token, role, resource etc) //------------------------------ var result = rawParseBaseParamRequestAPI(req, true, null); if(!result.res_obj.result){ r3logger.elog(result.res_obj.message); resutil.errResponse(req, res, result.res_code); return; } var comparam = result.parameters; //------------------------------ // check arguments //------------------------------ // type, key parameter var restype = null; var reskeyname = null; if(!apiutil.isSafeEntity(req.query) || !apiutil.isSafeString(req.query.type)){ restype = comparam.keys.VALUE_STRING_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_STRING_TYPE, req.query.type)){ restype = comparam.keys.VALUE_STRING_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_OBJECT_TYPE, req.query.type)){ restype = comparam.keys.VALUE_OBJECT_TYPE; }else if(apiutil.compareCaseString(comparam.keys.VALUE_KEYS_TYPE, req.query.type)){ restype = comparam.keys.VALUE_KEYS_TYPE; // key name if(!apiutil.isSafeEntity(req.query) || !apiutil.isSafeString(req.query.keyname)){ r3logger.elog('HEAD request type=keys, but keyname(' + req.query.keyname + ') parameter is empty.'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } reskeyname = req.query.keyname; }else{ r3logger.elog('HEAD request type(' + req.query.type + ') parameter is wrong.'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } // role yrn/ip address/port for no token var clientip = null; var port = 0; var cuk = null; var role_yrn = null; if(null !== comparam.token_type && (apiutil.isSafeEntity(req.query.port) || apiutil.isSafeEntity(req.query.cuk) || apiutil.isSafeEntity(req.query.role))){ r3logger.elog('HEAD resource:port/cuk/role field is specified, but it is not allowed by no token : port=' + JSON.stringify(req.query.port) + ', cuk=' + JSON.stringify(req.query.cuk) + ', role=' + JSON.stringify(req.query.role)); resutil.errResponse(req, res, 400); // 400: Bad Request return; } if(null === comparam.token_type){ // role if(!apiutil.isSafeString(req.query.role)){ r3logger.elog('HEAD request does not have role yrn in post data.'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = apiutil.getSafeString(req.query.role); // ip clientip = apiutil.getClientIpAddress(req); if(!apiutil.isSafeString(clientip)){ r3logger.elog('HEAD request does not have ip address for client.'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } // port if(apiutil.isSafeString(req.query.port) && !isNaN(req.query.port)){ port = parseInt(req.query.port); }else{ port = 0; } // cuk if(apiutil.isSafeString(req.query.cuk) && apiutil.isSafeString(req.query.cuk.trim())){ cuk = req.query.cuk.trim(); } } //------------------------------ // Run //------------------------------ if('user' === comparam.token_type){ result = k2hr3.checkResource(comparam.user_name, comparam.tenant_name, comparam.res_service, comparam.res_name, restype, reskeyname); }else if('role' === comparam.token_type){ // [NOTE] // comparam.token_info.role is role full yrn path, it does not include service name. // Because we do not put role token for role under service path. // result = k2hr3.checkResourceByRole(comparam.token_info.role, comparam.tenant_name, comparam.res_yrn, restype, reskeyname); }else if(null === comparam.token_type){ // [NOTE] // role_yrn allows a path containing service. // However, the case is rare and should not be used. // result = k2hr3.checkResourceByIP(clientip, port, cuk, role_yrn, comparam.res_yrn, restype, reskeyname); }else{ // broken token r3logger.elog('HEAD request is failure by internal error(token data broken).'); resutil.errResponse(req, res, 500); // 500: Internal Error return; } if(!apiutil.isSafeEntity(result) || !apiutil.isSafeEntity(result.result) || false === result.result){ if(!apiutil.isSafeEntity(result)){ result = { result: false, message: 'HEAD Could not get response from checkResource' }; }else{ if(!apiutil.isSafeEntity(result.result)){ result.result = false; } if(!apiutil.isSafeEntity(result.message)){ result.message = 'HEAD Could not get error message in response from checkResource'; } } r3logger.elog(result.message); resutil.errResponse(req, res, 403); // 403: Forbidden return; } r3logger.dlog('succeed : ' + result.message); res.status(204); // 204: No Content res.send(); }); // // Mountpath : '/v1/resource/*' // // DELETE '/v1/resource/name' : get resource on version 1 // HEADER : X-Auth-Token = User token // URL arguments : type = null(all), "anytype"(=any type data), "string"(=only string data), "object"(=only object data), "keys"(=key), "aliases"(=aliases) // keynames = null or keyname string or keyname array when type="keys" // aliases = null or alias key string or alias key array when type="aliases" // // DELETE '/v1/resource/name' : get resource on version 1 // HEADER : X-Auth-Token = Role token // URL arguments : type = "anytype"(=any type data), "string"(=only string data), "object"(=only object data), "keys"(=key) // keynames = null or keyname string or keyname array when type="keys" // // DELETE '/v1/resource/name' : get resource on version 1 // HEADER : X-Auth-Token = undefined // URL arguments : port = port number(undefined is default 0(any)) // cuk = container unique key(empty value, any value) // role = role full yrn // type = "anytype"(=any type data), "string"(=only string data), "object"(=only object data), "keys"(=key) // keynames = null or keyname string or keyname array when type="keys" // router.delete('/', function(req, res, next) // eslint-disable-line no-unused-vars { r3logger.dlog('CALL:', req.method, req.url); res.type('application/json; charset=utf-8'); if( !apiutil.isSafeEntity(req) || !apiutil.isSafeEntity(req.baseUrl) ) { r3logger.elog('DELETE request or url or query is wrong'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check common parameters(token, role, resource etc) //------------------------------ var result = rawParseBaseParamRequestAPI(req, false, null); if(!result.res_obj.result){ r3logger.elog(result.res_obj.message); resutil.errResponse(req, res, result.res_code); return; } var comparam = result.parameters; //------------------------------ // check resource(must be same tenant) //------------------------------ if(null !== comparam.token_type && (comparam.tenant_name !== comparam.res_tenant || apiutil.isSafeString(comparam.res_service))){ r3logger.elog('DELETE request resource(' + JSON.stringify(comparam.res_name) + ') is under tenant(' + JSON.stringify(comparam.res_tenant) + ') and service(' + JSON.stringify(comparam.res_service) + '), it is not under tenant(' + JSON.stringify(comparam.tenant_name) + ').'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // type, key parameter(role token/no token type) var datatype = null; var keynames = null; var aliases = null; if(!apiutil.isSafeEntity(req.query) || !apiutil.isSafeString(req.query.type)){ if('user' !== comparam.token_type){ r3logger.elog('DELETE request type=all(null), this type needs user token.'); resutil.errResponse(req, res, 400); // 400: Bad Request return; } datatype = null;