UNPKG

k2hr3-api

Version:

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

1,008 lines 73.5 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 k2hr3dkc_1 = __importDefault(require("../lib/k2hr3dkc")); const dbglogging_1 = __importDefault(require("../lib/dbglogging")); const express_1 = __importDefault(require("express")); const k2hr3keys_1 = require("../lib/k2hr3keys"); const r3keys = k2hr3keys_1.getK2hr3Keys; const router = express_1.default.Router(); // // 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 // }, // status: 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 // } // } // const rawParseBaseParamRequestAPI = (req, is_allow_service, default_resource_name) => { const parameters = { token_type: null, token_str: null, token_info: null, user_name: null, tenant_name: null, keys: r3keys(), res_yrn: null, res_name: null, res_tenant: null, res_service: null }; const result = { result: true, message: null, status: 200, parameters: parameters }; // // check token for API mode // if (k2hr3tokens_1.default.hasAuthTokenHeader(req)) { const token_result = k2hr3tokens_1.default.checkToken(req, true); // scoped, both token if (!token_result.result) { result.result = token_result.result; result.message = token_result.message; result.status = token_result.status; dbglogging_1.default.elog(result.message); return result; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { result.result = false; result.message = 'specified wrong token or it is not scoped user token'; result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } result.parameters.token_type = k2hr3apiutil_1.default.isSafeString(token_result.token_type) ? token_result.token_type : null; result.parameters.token_str = k2hr3apiutil_1.default.isSafeString(token_result.token) ? token_result.token : null; result.parameters.token_info = token_info; result.parameters.user_name = k2hr3apiutil_1.default.getSafeString(token_info.user); result.parameters.tenant_name = k2hr3apiutil_1.default.getSafeString(token_info.tenant).toLowerCase(); result.parameters.keys = r3keys(token_info.user, token_info.tenant); } const tmpTenant = k2hr3tokens_1.default.isResTypeCheckRoleToken(result.parameters.token_info) ? (k2hr3apiutil_1.default.isSafeString(result.parameters.token_info.tenant) ? result.parameters.token_info.tenant : null) : null; const tmpUser = k2hr3tokens_1.default.isResTypeCheckRoleToken(result.parameters.token_info) ? (k2hr3apiutil_1.default.isSafeString(result.parameters.token_info.user) ? result.parameters.token_info.user : null) : null; // // check service parameter in request // let service_param = null; if (is_allow_service) { if (k2hr3apiutil_1.default.compareCaseString('POST', req.method)) { if (k2hr3apiutil_1.default.isPlainObject(req.body) && k2hr3apiutil_1.default.isSafeString(req.body.service)) { service_param = k2hr3apiutil_1.default.getSafeString(req.body.service).trim(); } } else { if (k2hr3apiutil_1.default.isPlainObject(req.query) && k2hr3apiutil_1.default.isSafeString(req.query.service)) { service_param = k2hr3apiutil_1.default.getSafeString(req.query.service).trim(); } } } // // get resource full yrn // const requestptn = new RegExp('^/v1/resource/(.*)'); // regex = /^\/v1\/resource\/(.*)/ 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])) { if (!k2hr3apiutil_1.default.isSafeString(default_resource_name)) { result.result = false; result.message = 'Default resource name is not specified or wrong value : ' + JSON.stringify(default_resource_name); result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } result.parameters.res_yrn = k2hr3apiutil_1.default.getSafeString(default_resource_name).toLowerCase(); } else { result.parameters.res_yrn = reqmatchs[1].toLowerCase(); } // // make resource name from resource yrn // let nameptn = new RegExp('^' + result.parameters.keys.MATCH_ANY_TENANT_RESOURCE); // regex = /^yrn:yahoo:(.*)::(.*):resource:(.*)/ const namematchs = result.parameters.res_yrn.match(nameptn); if (!k2hr3apiutil_1.default.isStringArray(namematchs) || !k2hr3apiutil_1.default.isNotEmptyArray(namematchs) || namematchs.length < 4) { // res_yrn is not full yrn to resource, then check wrong resource name nameptn = new RegExp('^' + result.parameters.keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (result.parameters.res_yrn.match(nameptn)) { result.result = false; result.message = 'Request query has wrong yrn full path to resource'; result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } // no token need full yrn to resource(other token has tenant name) if (!k2hr3apiutil_1.default.isSafeString(result.parameters.token_type)) { result.result = false; result.message = 'Request query does not have yrn full path to resource'; result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } // make resource yrn from resource name(sometimes, a case of user token come here.) result.parameters.res_name = result.parameters.res_yrn; result.parameters.res_tenant = result.parameters.tenant_name; // resource is only name, then resource's tenant is same. result.parameters.res_service = k2hr3apiutil_1.default.isSafeString(service_param) ? service_param.toLowerCase() : null; result.parameters.keys = r3keys(tmpUser, result.parameters.res_tenant, result.parameters.res_service); result.parameters.res_yrn = result.parameters.keys.RESOURCE_TOP_KEY + ':' + result.parameters.res_name; } else { // res_yrn is full yrn to resource, then need to check tenant name if (k2hr3apiutil_1.default.isSafeString(result.parameters.token_type) && !k2hr3apiutil_1.default.compareCaseString(namematchs[2], tmpTenant)) { result.result = false; result.message = 'Request query has wrong yrn full path(tenant=' + namematchs[2] + ') to resource(tenant=' + k2hr3apiutil_1.default.getSafeString(tmpTenant) + ')'; result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } // check service name if (k2hr3apiutil_1.default.isSafeString(service_param) && !k2hr3apiutil_1.default.compareCaseString(service_param, namematchs[1])) { result.result = false; result.message = 'Request query has service name(' + service_param + ') and path has service name(' + namematchs[1] + '), but both are not same service name.'; result.status = 400; // 400: Bad Request dbglogging_1.default.elog(result.message); return result; } result.parameters.res_name = namematchs[3].toLowerCase(); result.parameters.res_tenant = namematchs[2].toLowerCase(); // resource is yrn, then resource's tenant is set from yrn. result.parameters.res_service = namematchs[1].toLowerCase(); // resource is not yrn, then service is not specified. result.parameters.keys = r3keys(tmpUser, tmpTenant, result.parameters.res_service); } 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('/', (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.body) || !k2hr3apiutil_1.default.isPlainObject(req.body.resource)) { const result = { result: false, message: 'POST body does not have resource data' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check common parameters(token, role, resource etc) //------------------------------ const resobj = rawParseBaseParamRequestAPI(req, false, k2hr3apiutil_1.default.isSafeString(req.body.resource.name) ? req.body.resource.name : null); if (!resobj.result) { const result = { result: false, message: resobj.message }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, resobj.status, result); return; } const comparam = resobj.parameters; //------------------------------ // check resource //------------------------------ if (k2hr3apiutil_1.default.isString(comparam.token_type) && (k2hr3apiutil_1.default.isSafeString(comparam.tenant_name) !== k2hr3apiutil_1.default.isSafeString(comparam.res_tenant) || k2hr3apiutil_1.default.getSafeString(comparam.tenant_name) !== k2hr3apiutil_1.default.getSafeString(comparam.res_tenant) || k2hr3apiutil_1.default.isSafeString(comparam.res_service))) { const result = { result: false, message: '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) + ').' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // data type let type; if (k2hr3apiutil_1.default.isSafeString(req.body.resource.type)) { type = k2hr3apiutil_1.default.getSafeString(req.body.resource.type); if (!k2hr3apiutil_1.default.compareCaseString('string', type) && !k2hr3apiutil_1.default.compareCaseString('object', type)) { const result = { result: false, message: 'POST resource:type field is wrong : ' + JSON.stringify(req.body.resource.type) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } else { type = comparam.keys.VALUE_STRING_TYPE; // default type is string } // data let data = null; if (k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_STRING_TYPE, type)) { if (k2hr3apiutil_1.default.isString(req.body.resource.data) && '' === req.body.resource.data) { data = ''; } else if (k2hr3apiutil_1.default.isSafeString(req.body.resource.data)) { // data is string(nodejs is decodeURI automatically) // this value includes control codes(\n, etc) data = k2hr3apiutil_1.default.getSafeString(req.body.resource.data); } } else { // type == object if (k2hr3apiutil_1.default.isValTypeAll(req.body.resource.data)) { data = req.body.resource.data; } } // keys let resource_keys = null; if (k2hr3apiutil_1.default.isSafeEntity(req.body.resource.keys)) { if (k2hr3apiutil_1.default.isString(req.body.resource.keys) && '' === req.body.resource.keys) { resource_keys = ''; } else if (k2hr3dkc_1.default.isDkcTypeResourceRawKeysValue(req.body.resource.keys)) { resource_keys = req.body.resource.keys; } else if (k2hr3apiutil_1.default.isSafeString(req.body.resource.keys)) { const tmp_keys = k2hr3apiutil_1.default.getSafeString(req.body.resource.keys); if (k2hr3apiutil_1.default.checkSimpleJSON(tmp_keys)) { const tmp_parsed_keys = JSON.parse(tmp_keys); if (k2hr3apiutil_1.default.isSafeString(tmp_parsed_keys)) { resource_keys = tmp_parsed_keys; } else if (k2hr3dkc_1.default.isDkcTypeResourceRawKeysValue(tmp_parsed_keys)) { resource_keys = tmp_parsed_keys; } else { resource_keys = null; } } else { resource_keys = tmp_keys; } } else { const result = { result: false, message: 'POST resource:keys field is wrong : ' + JSON.stringify(req.body.resource.keys) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } // alias if ('user' !== k2hr3apiutil_1.default.getSafeString(comparam.token_type) && k2hr3apiutil_1.default.isSafeEntity(req.body.resource.alias)) { const 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) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let aliases = null; if ('user' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { const aliasptn = new RegExp('^' + comparam.keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.resource.alias, aliasptn, null); if (false === aliaspram.result) { const result = { result: false, message: 'POST resource:alias field is wrong : ' + JSON.stringify(req.body.resource.alias) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } aliases = aliaspram.parameter ?? null; } // role yrn/ip address/port for no token let clientip = null; let port = 0; let cuk = null; let role_yrn = null; if (k2hr3apiutil_1.default.isSafeString(comparam.token_type) && (k2hr3apiutil_1.default.isSafeEntity(req.body.resource.port) || k2hr3apiutil_1.default.isSafeEntity(req.body.resource.cuk) || k2hr3apiutil_1.default.isSafeEntity(req.body.resource.role))) { const 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) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type)) { // role if (!k2hr3apiutil_1.default.isSafeString(req.body.resource.role)) { const result = { result: false, message: 'POST request does not have role yrn in post data.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = k2hr3apiutil_1.default.getSafeString(req.body.resource.role); // ip clientip = k2hr3apiutil_1.default.getClientIpAddress(req); if (!k2hr3apiutil_1.default.isSafeString(clientip)) { const result = { result: false, message: 'POST request does not have ip address for client.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // port if (k2hr3apiutil_1.default.isSafeNumeric(req.body.resource.port)) { const tmpPort = k2hr3apiutil_1.default.cvtToNumber(req.body.resource.port); if (k2hr3apiutil_1.default.isSafeNumber(tmpPort)) { port = tmpPort; } else { port = 0; } } else { port = 0; } // cuk if (k2hr3apiutil_1.default.isSafeString(req.body.resource.cuk) && k2hr3apiutil_1.default.isSafeString(req.body.resource.cuk.trim())) { cuk = req.body.resource.cuk.trim(); } } //------------------------------ // set all field to resource //------------------------------ let res_result; if ('user' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAll(comparam.user_name, comparam.tenant_name, comparam.res_name, type, data, resource_keys, aliases); } else if ('role' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAllByRole((comparam.token_info?.role ?? null), comparam.tenant_name, comparam.res_name, type, data, resource_keys); } else if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAllByIP(clientip, port, cuk, role_yrn, comparam.res_name, type, data, resource_keys); } else { // broken token const result = { result: false, message: 'POST request is failure by internal error(token data broken).' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 500, result); // 500: Internal Error return; } if (!k2hr3apiutil_1.default.isPlainObject(res_result) || !k2hr3apiutil_1.default.isBoolean(res_result.result) || false === res_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(res_result)) { const result = { result: false, message: 'POST Could not get response from setResourceAll' }; 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.isSafeString(res_result.message) ? res_result.message : 'POST Could not get error message in response from setResourceAll' }; 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(res_result.message)); const success_result = { result: true, message: null }; res.status(201); // 201: Created res.send(JSON.stringify(success_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('/', (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 common parameters(token, role, resource etc) //------------------------------ const resobj = rawParseBaseParamRequestAPI(req, false, (k2hr3apiutil_1.default.isString(req.query.name) ? req.query.name : null)); if (!resobj.result) { const result = { result: false, message: resobj.message }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, resobj.status, result); return; } const comparam = resobj.parameters; //------------------------------ // check resource //------------------------------ if (k2hr3apiutil_1.default.isString(comparam.token_type) && (k2hr3apiutil_1.default.isSafeString(comparam.tenant_name) !== k2hr3apiutil_1.default.isSafeString(comparam.res_tenant) || k2hr3apiutil_1.default.getSafeString(comparam.tenant_name) !== k2hr3apiutil_1.default.getSafeString(comparam.res_tenant) || k2hr3apiutil_1.default.isSafeString(comparam.res_service))) { const result = { result: false, message: '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) + ').' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // data type let type; if (k2hr3apiutil_1.default.isSafeString(req.query.type)) { type = k2hr3apiutil_1.default.getSafeString(req.query.type); if (!k2hr3apiutil_1.default.compareCaseString('string', type) && !k2hr3apiutil_1.default.compareCaseString('object', type)) { const result = { result: false, message: 'PUT resource:type field is wrong : ' + JSON.stringify(req.query.type) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } else { type = comparam.keys.VALUE_STRING_TYPE; // default type is string } // data let data = null; if (k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_STRING_TYPE, type)) { if (k2hr3apiutil_1.default.isString(req.query.data) && '' === req.query.data) { data = ''; } else if (k2hr3apiutil_1.default.isSafeString(req.query.data)) { const tmpData = k2hr3apiutil_1.default.getSafeString(req.query.data); if (k2hr3apiutil_1.default.checkSimpleJSON(tmpData)) { data = JSON.parse(tmpData); // decode JSON } else if (k2hr3apiutil_1.default.isSafeString(tmpData)) { data = tmpData; } } } else { // type == object if (k2hr3apiutil_1.default.isSafeString(req.query.data)) { const tmpData = k2hr3apiutil_1.default.getSafeString(req.query.data); if (k2hr3apiutil_1.default.checkSimpleJSON(tmpData)) { data = JSON.parse(tmpData); // decode JSON } else if (k2hr3apiutil_1.default.isSafeString(tmpData)) { data = tmpData; } } } // keys let resource_keys = null; if (k2hr3apiutil_1.default.isString(req.query.keys) && '' === req.query.keys) { resource_keys = ''; } else if (k2hr3apiutil_1.default.isSafeString(req.query.keys)) { // keys is encoded by JSON, this value is associative array. // const tmp_keys = k2hr3apiutil_1.default.getSafeString(req.query.keys); if (k2hr3apiutil_1.default.checkSimpleJSON(tmp_keys)) { const tmp_parsed_keys = JSON.parse(tmp_keys); if (k2hr3apiutil_1.default.isSafeString(tmp_parsed_keys)) { resource_keys = tmp_parsed_keys; } else if (k2hr3dkc_1.default.isDkcTypeResourceRawKeysValue(tmp_parsed_keys)) { resource_keys = tmp_parsed_keys; } else { resource_keys = null; } } else { resource_keys = tmp_keys; } } // alias if ('user' !== k2hr3apiutil_1.default.getSafeString(comparam.token_type) && k2hr3apiutil_1.default.isSafeEntity(req.query.alias)) { const result = { result: false, message: 'PUT resource:alias field is specified, but it is not allowed by not user token : ' + JSON.stringify(req.query.alias) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let aliases = null; if ('user' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { if (k2hr3apiutil_1.default.isString(req.query.alias) && '' === req.query.alias) { aliases = ''; } else if (k2hr3apiutil_1.default.isSafeString(req.query.alias)) { // alias is encoded by JSON, this value is array. // let tmpaliases = k2hr3apiutil_1.default.getSafeString(req.query.alias); if (k2hr3apiutil_1.default.checkSimpleJSON(tmpaliases)) { tmpaliases = JSON.parse(tmpaliases); } const aliasptn = new RegExp('^' + comparam.keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(tmpaliases, aliasptn, null); if (false === aliaspram.result) { const result = { result: false, message: 'PUT resource: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; } } // role yrn/ip address/port for no token let clientip = null; let port = 0; let cuk = null; let role_yrn = null; if (k2hr3apiutil_1.default.isSafeString(comparam.token_type) && (k2hr3apiutil_1.default.isSafeEntity(req.query.port) || k2hr3apiutil_1.default.isSafeEntity(req.query.cuk) || k2hr3apiutil_1.default.isSafeEntity(req.query.role))) { const 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) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type)) { // role if (!k2hr3apiutil_1.default.isSafeString(req.query.role)) { const result = { result: false, message: 'PUT request does not have role yrn in post data.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // [NOTE] // not check role is full yrn here. role_yrn = k2hr3apiutil_1.default.getSafeString(req.query.role); // ip clientip = k2hr3apiutil_1.default.getClientIpAddress(req); if (!k2hr3apiutil_1.default.isSafeString(clientip)) { const result = { result: false, message: 'PUT request does not have ip address for client.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // port if (k2hr3apiutil_1.default.isSafeNumeric(req.query.port)) { const tmpPort = k2hr3apiutil_1.default.cvtToNumber(req.query.port); if (k2hr3apiutil_1.default.isSafeNumber(tmpPort)) { port = tmpPort; } else { port = 0; } } else { port = 0; } // cuk if (k2hr3apiutil_1.default.isSafeString(req.query.cuk) && k2hr3apiutil_1.default.isSafeString(req.query.cuk.trim())) { cuk = req.query.cuk.trim(); } } //------------------------------ // set all field to resource //------------------------------ let res_result; if ('user' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAll(comparam.user_name, comparam.tenant_name, comparam.res_name, type, data, resource_keys, aliases); } else if ('role' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAllByRole((comparam.token_info?.role ?? null), comparam.tenant_name, comparam.res_name, type, data, resource_keys); } else if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type)) { res_result = k2hr3dkc_1.default.setResourceAllByIP(clientip, port, cuk, role_yrn, comparam.res_name, type, data, resource_keys); } else { // broken token const result = { result: false, message: 'PUT request is failure by internal error(token data broken).' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 500, result); // 500: Internal Error return; } if (!k2hr3apiutil_1.default.isPlainObject(res_result) || !k2hr3apiutil_1.default.isBoolean(res_result.result) || false === res_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(res_result)) { const result = { result: false, message: 'PUT Could not get response from setResourceAll' }; 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.isSafeEntity(res_result.message) ? res_result.message : 'PUT Could not get error message in response from setResourceAll' }; 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(res_result.message)); const success_result = { result: true, message: null }; res.status(201); // 201: Created res.send(JSON.stringify(success_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('/', (req, res, next) => { dbglogging_1.default.dlog('CALL:', req.method, req.url, req.baseUrl); 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'); if (!k2hr3apiutil_1.default.isPlainObject(req) || !k2hr3apiutil_1.default.isSafeString(req.baseUrl)) { const result = { result: false, message: 'GET request or url is wrong' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check common parameters(token, role, resource etc) //------------------------------ const resobj = rawParseBaseParamRequestAPI(req, true, null); if (!resobj.result) { const result = { result: false, message: resobj.message }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, resobj.status, result); return; } const comparam = resobj.parameters; //------------------------------ // check arguments //------------------------------ // expand type(only user token type) let is_expand = true; if (k2hr3apiutil_1.default.isPlainObject(req.query) && k2hr3apiutil_1.default.isSafeString(req.query.expand)) { if ('user' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { if (k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_TRUE, req.query.expand)) { is_expand = true; } else if (k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_FALSE, req.query.expand)) { is_expand = false; } else { const 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 + '.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } else { dbglogging_1.default.wlog('GET found unnessesary expand(' + JSON.stringify(req.query.expand) + ') parameter, skip this.'); } } // type, key parameter(role token/no token type) let restype = null; let reskeyname = null; if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type) || 'role' === k2hr3apiutil_1.default.getSafeString(comparam.token_type)) { if (!k2hr3apiutil_1.default.isPlainObject(req.query) || !k2hr3apiutil_1.default.isSafeString(req.query.type)) { restype = comparam.keys.VALUE_STRING_TYPE; } else if (k2hr3apiutil_1.default.isSafeString(req.query.type) && k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_STRING_TYPE, req.query.type)) { restype = comparam.keys.VALUE_STRING_TYPE; } else if (k2hr3apiutil_1.default.isSafeString(req.query.type) && k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_OBJECT_TYPE, req.query.type)) { restype = comparam.keys.VALUE_OBJECT_TYPE; } else if (k2hr3apiutil_1.default.isSafeString(req.query.type) && k2hr3apiutil_1.default.compareCaseString(comparam.keys.VALUE_KEYS_TYPE, req.query.type)) { restype = comparam.keys.VALUE_KEYS_TYPE; // key name if (!k2hr3apiutil_1.default.isPlainObject(req.query) || !k2hr3apiutil_1.default.isSafeString(req.query.keyname)) { const result = { result: false, message: 'GET request type=keys, but keyname(' + req.query.keyname + ') parameter is empty.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } reskeyname = req.query.keyname; } else { const result = { result: false, message: 'GET request type(' + req.query.type + ') parameter is wrong.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } // role yrn/ip address/port for no token let clientip = null; let port = 0; let cuk = null; let role_yrn = null; if (k2hr3apiutil_1.default.isSafeString(comparam.token_type) && (k2hr3apiutil_1.default.isSafeEntity(req.query.port) || k2hr3apiutil_1.default.isSafeEntity(req.query.cuk) || k2hr3apiutil_1.default.isSafeEntity(req.query.role))) { const 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) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } if (!k2hr3apiutil_1.default.isSafeString(comparam.token_type)) { // role if (!k2hr3apiutil_1.default.isSafeString(req.query.role)) { const result = { result: false, message: 'GET request does not have role yrn in post data.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default