k2hr3-api
Version:
K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules
1,008 lines • 73.5 kB
JavaScript
"use strict";
/*
* K2HR3 REST API
*
* Copyright 2017 Yahoo Japan Corporation.
*
* K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
* common management information for the cloud.
* K2HR3 can dynamically manage information as "who", "what", "operate".
* These are stored as roles, resources, policies in K2hdkc, and the
* client system can dynamically read and modify these information.
*
* For the full copyright and license information, please view
* the license file that was distributed with this source code.
*
* AUTHOR: Takeshi Nakatani
* CREATE: Wed Jun 8 2017
* REVISION:
*
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const k2hr3apiutil_1 = __importDefault(require("../lib/k2hr3apiutil"));
const k2hr3resutil_1 = __importDefault(require("../lib/k2hr3resutil"));
const k2hr3tokens_1 = __importDefault(require("../lib/k2hr3tokens"));
const 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