UNPKG

k2hr3-api

Version:

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

841 lines (840 loc) 41.4 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(); // Mountpath : '/v1/policy' // POST '/v1/policy' : post policy on version 1 // response body : result => true/false // message => messages // body : // { // "policy": { // "name": <policy name> => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>" // "effect": "allow" or "deny" => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/effect" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set default(default deny).. // "action": [<action yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/action" // specify "yrn:yahoo::::action:read" or "yrn:yahoo::::action:write" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // "resource": [<resource yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/resource" // specify "yrn:yahoo:<service>::<tenant>:resource:<resource>{/<resource>{...}}" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // "condition": null or undefined => this member is reserved on v1, must be null or undefined. // "alias": [<policy yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/@" // specify another policy as "yrn:yahoo:<service>::<tenant>:policy:<policy>" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // } // } // 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.policy)) { const result = { result: false, message: 'POST body does not have policy data' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token if (!token_result.result) { const result = { result: token_result.result, message: k2hr3apiutil_1.default.getSafeString(token_result.message), }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, token_result.status, result); return; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { const result = { result: false, message: 'specified wrong token or it is not scoped user token' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ if (!k2hr3apiutil_1.default.isSafeString(req.body.policy.name)) { const result = { result: false, message: 'policy:name field is wrong : ' + JSON.stringify(req.body.policy.name) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const keys = r3keys(token_info.user, token_info.tenant); let name = k2hr3apiutil_1.default.getSafeString(req.body.policy.name).toLowerCase(); // policy name is only name or full yrn path let nameptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ const namematchs = name.match(nameptn); if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) { name = namematchs[1]; } // check token's tenant(if same tenant, name is not full yrn) nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (name.match(nameptn)) { const result = { result: false, message: 'POST request url has wrong yrn full path to policy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let effect; if (!k2hr3apiutil_1.default.isSafeEntity(req.body.policy.effect)) { effect = null; // = not update if policy exists } else if (k2hr3apiutil_1.default.isString(req.body.policy.effect) && '' === req.body.policy.effect) { effect = false; // = deny } else if (k2hr3apiutil_1.default.isString(req.body.policy.effect) && k2hr3apiutil_1.default.compareCaseString(req.body.policy.effect, keys.VALUE_ALLOW)) { effect = true; // = allow } else if (k2hr3apiutil_1.default.isString(req.body.policy.effect) && k2hr3apiutil_1.default.compareCaseString(req.body.policy.effect, keys.VALUE_DENY)) { effect = false; // = deny } else { const result = { result: false, message: 'policy:effect field is wrong : ' + JSON.stringify(req.body.policy.effect) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const actptns = [keys.ACTION_READ_KEY, keys.ACTION_WRITE_KEY]; // allow string as read/write yrn full path const actpram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.policy.action, null, actptns); if (false === actpram.result) { const result = { result: false, message: 'policy:action field is wrong : ' + JSON.stringify(req.body.policy.action) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const actions = actpram.parameter; const resptn = new RegExp('^' + keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ const respram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.policy.resource, resptn, null); if (false === respram.result) { const result = { result: false, message: 'policy:resource field is wrong : ' + JSON.stringify(req.body.policy.resource) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const resources = respram.parameter; const condition = null; // now reserved this field if (null !== req.body.policy.condition && undefined !== req.body.policy.condition && !k2hr3apiutil_1.default.isSafeString(req.body.policy.condition)) { const result = { result: false, message: 'policy:condition field is wrong : ' + JSON.stringify(req.body.policy.condition) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const aliasptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(req.body.policy.alias, aliasptn, null); if (false === aliaspram.result) { const result = { result: false, message: 'policy:alias field is wrong : ' + JSON.stringify(req.body.policy.alias) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const aliases = aliaspram.parameter; //------------------------------ // set all field to policy //------------------------------ const pol_result = k2hr3dkc_1.default.setPolicyAll(token_info.user, token_info.tenant, name, effect, actions, resources, condition, aliases); if (!k2hr3apiutil_1.default.isPlainObject(pol_result) || !k2hr3apiutil_1.default.isBoolean(pol_result.result) || false === pol_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(pol_result)) { const result = { result: false, message: 'Could not get response from setPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } else { const result = { result: k2hr3apiutil_1.default.isBoolean(pol_result.result) ? pol_result.result : false, message: k2hr3apiutil_1.default.isString(pol_result.message) ? pol_result.message : 'Could not get error message in response from setPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } return; } dbglogging_1.default.dlog('succeed : ' + pol_result.message); res.status(201); // 201: Created res.send(JSON.stringify(pol_result)); }); // Mountpath : '/v1/policy' // PUT '/v1/policy' : put policy on version 1 // response body : result => true/false // message => messages // url argument // "name" : <policy name> => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>" // "effect" : "allow" or "deny" => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/effect" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set default(default deny).. // "action" : [<action yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/action" // specify "yrn:yahoo::::action:read" or "yrn:yahoo::::action:write" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // "resource" : [<resource yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/resource" // specify "yrn:yahoo:<service>::<tenant>:resource:<resource>{/<resource>{...}}" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // "condition" : null or undefined => this member is reserved on v1, must be null or undefined. // "alias" : [<policy yrn full path>, ...] => key is "yrn:yahoo:<service>::<tenant>:policy:<policy>/@" // specify another policy as "yrn:yahoo:<service>::<tenant>:policy:<policy>" // if null or undefined is specified, not update this member in policy when this policy exists. // if '' or zero array, this member in policy is set empty array. // 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.isSafeEntity(req.query)) { const result = { result: false, message: 'PUT argument does not have any data' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token if (!token_result.result) { const result = { result: token_result.result, message: k2hr3apiutil_1.default.getSafeString(token_result.message), }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, token_result.status, result); return; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { const result = { result: false, message: 'specified wrong token or it is not scoped user token' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ if (!k2hr3apiutil_1.default.isSafeString(req.query.name)) { const result = { result: false, message: 'policy:name field is wrong : ' + JSON.stringify(req.query.name) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const keys = r3keys(token_info.user, token_info.tenant); let name = k2hr3apiutil_1.default.getSafeString(req.query.name).toLowerCase(); // policy name is only name or full yrn path let nameptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ const namematchs = name.match(nameptn); if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) { name = namematchs[1]; } // check token's tenant(if same tenant, name is not full yrn) nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (name.match(nameptn)) { const result = { result: false, message: 'PUT request url has wrong yrn full path to policy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let effect; if (!k2hr3apiutil_1.default.isString(req.query.effect)) { effect = null; // = not update if policy exists } else if (!k2hr3apiutil_1.default.isSafeString(req.query.effect)) { effect = false; // = deny } else if (k2hr3apiutil_1.default.compareCaseString(req.query.effect, keys.VALUE_ALLOW)) { effect = true; // = allow } else if (k2hr3apiutil_1.default.compareCaseString(req.query.effect, keys.VALUE_DENY)) { effect = false; // = deny } else { const result = { result: false, message: 'policy:effect field is wrong : ' + JSON.stringify(req.query.effect) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const actptns = [keys.ACTION_READ_KEY, keys.ACTION_WRITE_KEY]; // allow string as read/write yrn full path const actpram = k2hr3apiutil_1.default.getNormalizeParameter(req.query.action, null, actptns); if (false === actpram.result) { const result = { result: false, message: 'policy:action field is wrong : ' + JSON.stringify(req.query.action) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const actions = actpram.parameter; const resptn = new RegExp('^' + keys.RESOURCE_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:resource:(.*)/ const respram = k2hr3apiutil_1.default.getNormalizeParameter(req.query.resource, resptn, null); if (false === respram.result) { const result = { result: false, message: 'policy:resource field is wrong : ' + JSON.stringify(req.query.resource) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const resources = respram.parameter; const condition = null; // now reserved this field if (null !== req.query.condition && undefined !== req.query.condition && !k2hr3apiutil_1.default.isSafeString(req.query.condition)) { const result = { result: false, message: 'policy:condition field is wrong : ' + JSON.stringify(req.query.condition) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const aliasptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ const aliaspram = k2hr3apiutil_1.default.getNormalizeParameter(req.query.alias, aliasptn, null); if (false === aliaspram.result) { const result = { result: false, message: 'policy:alias field is wrong : ' + JSON.stringify(req.query.alias) }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const aliases = aliaspram.parameter; //------------------------------ // set all field to policy //------------------------------ const pol_result = k2hr3dkc_1.default.setPolicyAll(token_info.user, token_info.tenant, name, effect, actions, resources, condition, aliases); if (!k2hr3apiutil_1.default.isPlainObject(pol_result) || !k2hr3apiutil_1.default.isBoolean(pol_result.result) || false === pol_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(pol_result)) { const result = { result: false, message: 'Could not get response from setPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } else { const result = { result: k2hr3apiutil_1.default.isBoolean(pol_result.result) ? pol_result.result : false, message: k2hr3apiutil_1.default.isString(pol_result.message) ? pol_result.message : 'Could not get error message in response from setPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } return; } dbglogging_1.default.dlog('succeed : ' + pol_result.message); res.status(201); // 201: Created res.send(JSON.stringify(pol_result)); }); // Mountpath : '/v1/policy/*' // GET '/v1/policy/name' : get policy on version 1(name is allowed full yrn path) // URL arguments : service => undefined or service name // response body : result => true/false // message => error message // policy => object // policy object // { // "name": <policy name> // "effect": "allow" or "deny" // "action": [<action yrn full path>, ...] // "resource": [<resource yrn full path>, ...] // "condition": null or undefined // "alias": [<policy yrn full path>, ...] // } // 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; } //------------------------------ // service name //------------------------------ let service = null; if (k2hr3apiutil_1.default.isPlainObject(req.query) && k2hr3apiutil_1.default.isSafeString(req.query.service)) { service = k2hr3apiutil_1.default.getSafeString(req.query.service).toLowerCase(); } //------------------------------ // check token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token if (!token_result.result) { const result = { result: token_result.result, message: k2hr3apiutil_1.default.getSafeString(token_result.message), }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, token_result.status, result); return; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { const result = { result: false, message: 'specified wrong token or it is not scoped user token' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const keys = r3keys(token_info.user, token_info.tenant, service); //------------------------------ // check policy name //------------------------------ const requestptn = new RegExp('^/v1/policy/(.*)'); // regex = /^\/v1\/policy\/(.*)/ const reqmatchs = decodeURI(req.baseUrl).match(requestptn); if (!k2hr3apiutil_1.default.isStringArray(reqmatchs) || !k2hr3apiutil_1.default.isNotEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === k2hr3apiutil_1.default.getSafeString(reqmatchs[1])) { const result = { result: false, message: 'GET request url does not have policy name' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let name = reqmatchs[1].toLowerCase(); // // make policy name from policy yrn // let nameptn = new RegExp('^' + keys.MATCH_ANY_TENANT_POLICY); // regex = /^yrn:yahoo:(.*)::(.*):policy:(.*)/ const namematchs = name.match(nameptn); if (!k2hr3apiutil_1.default.isStringArray(namematchs) || !k2hr3apiutil_1.default.isNotEmptyArray(namematchs) || namematchs.length < 4) { // name is not full yrn to policy, then check wrong policy name nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (name.match(nameptn)) { const result = { result: false, message: 'Request query has wrong yrn full path to policy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // no token need full yrn to policy(other token has tenant name) if (!k2hr3apiutil_1.default.isString(token_result.token_type)) { const result = { result: false, message: 'Request query does not have yrn full path to policy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // no problem, name is policy name } else { // name is full yrn to policy, then need to check tenant name if (k2hr3apiutil_1.default.isString(token_result.token_type) && !k2hr3apiutil_1.default.compareCaseString(namematchs[2], token_info.tenant)) { const result = { result: false, message: 'Request query has wrong yrn full path(tenant=' + namematchs[2] + ') to policy(tenant=' + k2hr3apiutil_1.default.getSafeString(token_info.tenant) + ')' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // check service name if (k2hr3apiutil_1.default.isSafeString(service)) { if (!k2hr3apiutil_1.default.compareCaseString(service, namematchs[1])) { const result = { result: false, message: 'Request query has service name(' + (service ?? '') + ') and path has service name(' + namematchs[1] + '), but both are not same service name.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } } else if (k2hr3apiutil_1.default.isSafeString(namematchs[1])) { // set service name service = k2hr3apiutil_1.default.getSafeString(namematchs[1]).trim().toLowerCase(); //keys = r3keys(token_info.user, token_info.tenant, service); } // set name name = namematchs[3].toLowerCase(); } //------------------------------ // get all policy //------------------------------ const pol_result = k2hr3dkc_1.default.getPolicyAll(token_info.user, token_info.tenant, service, name); if (!k2hr3apiutil_1.default.isPlainObject(pol_result) || !k2hr3apiutil_1.default.isBoolean(pol_result.result) || false === pol_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(pol_result)) { const result = { result: false, message: 'Could not get response from getPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } else { const result = { result: k2hr3apiutil_1.default.isBoolean(pol_result.result) ? pol_result.result : false, message: k2hr3apiutil_1.default.isString(pol_result.message) ? pol_result.message : 'Could not get error message in response from getPolicyAll' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request } return; } dbglogging_1.default.dlog('succeed : ' + pol_result.message); res.status(200); // 200: OK res.send(JSON.stringify(pol_result)); }); // Mountpath : '/v1/policy/*' // HEAD '/v1/policy/name' : head policy on version 1(name is allowed full yrn path) // Url arguments // tenant : optional for policy/resource not full yrn // resource : resource to full yrn(or name) // action : action(read/write) // service : undefined or service name // router.head('/', (req, res, next) => { dbglogging_1.default.dlog('CALL:', req.method, req.url, req.baseUrl); 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 (!k2hr3apiutil_1.default.isPlainObject(req) || !k2hr3apiutil_1.default.isSafeString(req.baseUrl) || !k2hr3apiutil_1.default.isPlainObject(req.query)) { dbglogging_1.default.elog('HEAD request or url or query is wrong'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check arguments //------------------------------ // first tenant name let tenant = null; if (req.query.tenant && k2hr3apiutil_1.default.isSafeString(req.query.tenant)) { tenant = k2hr3apiutil_1.default.getSafeString(req.query.tenant).trim().toLowerCase(); } // service name let service = null; if (k2hr3apiutil_1.default.isSafeString(req.query.service)) { service = k2hr3apiutil_1.default.getSafeString(req.query.service).trim().toLowerCase(); } // keys let keys = r3keys(null, tenant, service); // policy name from path const requestptn = new RegExp('^/v1/policy/(.*)'); // regex = /^\/v1\/policy\/(.*)/ 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])) { dbglogging_1.default.elog('HEAD request url does not have policy name'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // check policy name is only name or full yrn path let name = reqmatchs[1].toLowerCase(); // // make policy name and full yrn path // let policy_yrn; let nameptn = new RegExp('^' + keys.MATCH_ANY_TENANT_POLICY); // regex = /^yrn:yahoo:(.*)::(.*):policy:(.*)/ const namematchs = name.match(nameptn); if (!k2hr3apiutil_1.default.isStringArray(namematchs) || !k2hr3apiutil_1.default.isNotEmptyArray(namematchs) || namematchs.length < 4) { // name is not full yrn to policy, then check wrong policy name nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (name.match(nameptn)) { dbglogging_1.default.elog('Request query has wrong yrn full path to policy'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // no token need full yrn to policy(other token has tenant name) if (!k2hr3apiutil_1.default.isSafeString(tenant)) { dbglogging_1.default.elog('Request query does not have yrn full path to policy'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // no problem, name is policy name policy_yrn = keys.POLICY_TOP_KEY + ':' + name; } else { // name is full yrn to policy, then need to check tenant name if (k2hr3apiutil_1.default.isSafeString(tenant) && !k2hr3apiutil_1.default.compareCaseString(namematchs[2], tenant)) { dbglogging_1.default.elog('Request query has wrong yrn full path(tenant=' + namematchs[2] + ') to policy(tenant=' + k2hr3apiutil_1.default.getSafeString(tenant) + ')'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // check service name if (k2hr3apiutil_1.default.isSafeString(service)) { if (!k2hr3apiutil_1.default.compareCaseString(service, namematchs[1])) { dbglogging_1.default.elog('Request query has service name(' + k2hr3apiutil_1.default.getSafeString(service) + ') and path has service name(' + namematchs[1] + '), but both are not same service name.'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } } else if (k2hr3apiutil_1.default.isSafeString(namematchs[1])) { // set service name service = k2hr3apiutil_1.default.getSafeString(namematchs[1]).trim().toLowerCase(); keys = r3keys(null, tenant, service); } // set name name = namematchs[3].toLowerCase(); policy_yrn = keys.POLICY_TOP_KEY + ':' + name; } // resource if (!k2hr3apiutil_1.default.isSafeString(req.query.resource)) { dbglogging_1.default.elog('HEAD request argument does not have resource parameter'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // check resource is only resource name or full yrn path let resource = k2hr3apiutil_1.default.getSafeString(req.query.resource); let resourceptn = new RegExp('^' + keys.MATCH_ANY_TENANT_RESOURCE); // regex = /^yrn:yahoo:(.*)::(.*):resource:(.*)/ const resourcematchs = resource.match(resourceptn); if (!k2hr3apiutil_1.default.isStringArray(resourcematchs) || !k2hr3apiutil_1.default.isNotEmptyArray(resourcematchs) || resourcematchs.length < 4) { // resource is not matched resource(maybe not full yrn), thus we need tenant parameter if (!k2hr3apiutil_1.default.isSafeString(tenant)) { dbglogging_1.default.elog('HEAD request query does not have resource by full yrn(if you want to set only resource name, you must specify tenant url argument)'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // if resource is yrn full path, then it is wrong policy resource resourceptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (resource.match(resourceptn)) { dbglogging_1.default.elog('HEAD request query has wrong yrn full path to resource'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // make full yrn for policy resource resource = keys.RESOURCE_TOP_KEY + ':' + resource; } // action if (!k2hr3apiutil_1.default.isSafeString(req.query.action)) { dbglogging_1.default.elog('HEAD request argument does not have action parameter'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } // check action is only action name or full yrn path let action = k2hr3apiutil_1.default.getSafeString(req.query.action); if (keys.VALUE_READ === action) { action = keys.ACTION_READ_KEY; } else if (keys.VALUE_WRITE === action) { action = keys.ACTION_WRITE_KEY; } else if (keys.ACTION_READ_KEY !== action && keys.ACTION_WRITE_KEY !== action) { dbglogging_1.default.elog('HEAD request query has wrong action value'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check policy //------------------------------ const result = k2hr3dkc_1.default.checkPolicy(policy_yrn, resource, action); if (!result.result) { dbglogging_1.default.dlog('action(' + JSON.stringify(action) + ') to resource(' + JSON.stringify(resource) + ') is not allowed by policy(' + JSON.stringify(name) + ') : message=' + k2hr3apiutil_1.default.getSafeString(result.message)); k2hr3resutil_1.default.errResponse(req, res, 403); // 403: Forbidden return; } dbglogging_1.default.dlog('action(' + JSON.stringify(action) + ') to resource(' + JSON.stringify(resource) + ') is allowed by policy(' + JSON.stringify(name) + ')'); res.status(204); // 204: No Content res.send(); }); // // Mountpath : '/v1/policy/*' // DELETE '/v1/policy/name' : delete policy on version 1 // router.delete('/', (req, res, _) => { dbglogging_1.default.dlog('CALL:', req.method, req.url, req.baseUrl); res.type('application/json; charset=utf-8'); if (!k2hr3apiutil_1.default.isPlainObject(req) || !k2hr3apiutil_1.default.isSafeString(req.baseUrl)) { dbglogging_1.default.elog('DELETE request or url or query is wrong'); k2hr3resutil_1.default.errResponse(req, res, 400); // 400: Bad Request return; } //------------------------------ // check token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(req, true, true); // scoped, user token if (!token_result.result) { const result = { result: token_result.result, message: k2hr3apiutil_1.default.getSafeString(token_result.message), }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, token_result.status, result); return; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { const result = { result: false, message: 'specified wrong token or it is not scoped user token' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } const keys = r3keys(token_info.user, token_info.tenant); //------------------------------ // check policy name //------------------------------ const requestptn = new RegExp('^/v1/policy/(.*)'); // regex = /^\/v1\/policy\/(.*)/ const reqmatchs = decodeURI(req.baseUrl).match(requestptn); if (!k2hr3apiutil_1.default.isStringArray(reqmatchs) || !k2hr3apiutil_1.default.isNotEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === k2hr3apiutil_1.default.getSafeString(reqmatchs[1])) { const result = { result: false, message: 'GET request url does not have policy name' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } let name = reqmatchs[1].toLowerCase(); // policy name is only name or full yrn path let nameptn = new RegExp('^' + keys.POLICY_TOP_KEY + ':(.*)'); // regex = /^yrn:yahoo:<service>::<tenant>:policy:(.*)/ const namematchs = name.match(nameptn); if (k2hr3apiutil_1.default.isStringArray(namematchs) && k2hr3apiutil_1.default.isNotEmptyArray(namematchs) && 2 <= namematchs.length) { name = namematchs[1]; } // check yrn full path(it is NG) nameptn = new RegExp('^' + keys.NO_TENANT_KEY); // regex = /^yrn:yahoo:/ if (name.match(nameptn)) { const result = { result: false, message: 'DELETE request url has wrong yrn full path to policy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // delete policy //------------------------------ const pol_result = k2hr3dkc_1.default.removePolicy(token_info.user, token_info.tenant, name); if (!k2hr3apiutil_1.default.isPlainObject(pol_result) || !k2hr3apiutil_1.default.isBoolean(pol_result.result) || false === pol_result.result) { if (!k2hr3apiutil_1.default.isPlainObject(pol_result)) { const result = { result: false, message: 'Could not get response from removePolicy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 403); // 403: Forbidden(is this status OK?) } else { const result = { result: k2hr3apiutil_1.default.isBoolean(pol_result.result) ? pol_result.result : false, message: k2hr3apiutil_1.default.isString(pol_result.message) ? pol_result.message : 'Could not get error message in response from removePolicy' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(req, res, 403); // 403: Forbidden(is this status OK?) } return; } dbglogging_1.default.dlog('succeed : ' + pol_result.message); res.status(204); // 204: No Content res.send(); }); //--------------------------------------------------------- // Exports //--------------------------------------------------------- // // Functions // exports.default = router; /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noexpandtab sw=4 ts=4 fdm=marker * vim<600: noexpandtab sw=4 ts=4 */