UNPKG

k2hr3-api

Version:

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

213 lines (212 loc) 9.48 kB
"use strict"; /* * K2HR3 REST API * * Copyright 2018 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: Tue Oct 2 2018 * 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 dbglogging_1 = __importDefault(require("../lib/dbglogging")); const k2hr3resutil_1 = __importDefault(require("../lib/k2hr3resutil")); const k2hr3userdata_1 = __importDefault(require("../lib/k2hr3userdata")); const express_1 = __importDefault(require("express")); const router = express_1.default.Router(); //--------------------------------------------------------- // Router GET //--------------------------------------------------------- // // Mountpath : '/v1/userdata/*' // // GET '/v1/userdata/<encrypted data>' : get userdata for openstack on version 1 // response : compressed(gzip) userdata(binary) for openstack // // This mount point is for getting compressed userdata of openstack which is // used at creating instance. // The response data is compressed by gzip, and it included multipart userdata. // The userdata probably has "cloud-config" and "script" for registering ip // address to k2hr3 role as its member. // The url last path part is url encoded string which is base64 and encrypted // role-token and role name. This userdata script will register ip address. // router.get('/', (req, res, next) => { dbglogging_1.default.dlog('CALL:', req.method, req.url); if ('GET' !== req.method) { // HEAD request comes here, so it should be routed to head(not defined) function. next(); return; } if (!k2hr3apiutil_1.default.isPlainObject(req) || !k2hr3apiutil_1.default.isSafeString(req.baseUrl) || !k2hr3apiutil_1.default.isPlainObject(req.headers)) // Must User-Agent in header { const result = { result: false, message: 'GET request or url is wrong' }; dbglogging_1.default.elog(result.message); res.type('application/json; charset=utf-8'); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } //------------------------------ // Check headers //------------------------------ let isGzip = false; const tmpAgent = req.headers['user-agent']; if (!k2hr3apiutil_1.default.isSafeEntity(tmpAgent)) { // 'User-Agent' Must have 'Cloud-Init' const result = { result: false, message: 'GET request does not have User-Agent header' }; dbglogging_1.default.elog(result.message); res.type('application/json; charset=utf-8'); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } else { const strtmp = tmpAgent.toLowerCase(); if (-1 == strtmp.indexOf('cloud-init')) { // 'User-Agent' Must have 'Cloud-Init' const result = { result: false, message: 'GET request is not allowed from your client' }; dbglogging_1.default.elog(result.message); res.type('application/json; charset=utf-8'); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // Check version const ciptn = new RegExp('cloud-init/([0-9]+).([0-9]+).([0-9]+)'); // regex = /Cloud-Init\/([0-9]+)\.([0-9]+)\.([0-9]+)/ const cimatchs = decodeURI(strtmp).match(ciptn); if (k2hr3apiutil_1.default.isStringArray(cimatchs) && k2hr3apiutil_1.default.isNotEmptyArray(cimatchs) && 3 < cimatchs.length) { const tmp_match_1 = k2hr3apiutil_1.default.cvtToNumber(cimatchs[1]); const tmp_match_2 = k2hr3apiutil_1.default.cvtToNumber(cimatchs[2]); const tmp_match_3 = k2hr3apiutil_1.default.cvtToNumber(cimatchs[3]); if ((k2hr3apiutil_1.default.isSafeNumber(tmp_match_1) && 0 < tmp_match_1) || (k2hr3apiutil_1.default.isSafeNumber(tmp_match_2) && 7 < tmp_match_2) || (k2hr3apiutil_1.default.isSafeNumber(tmp_match_3) && 9 <= tmp_match_3)) { // [FORCE] // Cloud-Init is 0.7.9 or after it, this version supports gzip compressed // userdata. Thus we return gzip compressed userdata. dbglogging_1.default.dlog('Cloud-Init version is over 0.7.9, thus we force return gzip compressed userdata'); isGzip = true; } } } const tmpContent = req.headers['content-type']; if (k2hr3apiutil_1.default.isSafeString(tmpContent)) { if (!k2hr3apiutil_1.default.hasPartString(tmpContent, ';', 'application/octet-stream', true)) { // should be 'application/octet-stream', but all type is allowed dbglogging_1.default.dlog('GET request Content-Type is not application/octet-stream, but continue...'); } } else { //r3logger.dlog('GET request doe not have Content-Type, but continue...'); } const tmpEnc = req.headers['accept-encoding']; if (k2hr3apiutil_1.default.isSafeEntity(tmpEnc)) { if (k2hr3apiutil_1.default.hasPartString(tmpEnc, ',', ['gzip', 'deflate'], true)) { isGzip = true; } else if (!isGzip) { // Accept-Encoding should have 'gzip' or 'deflate', but all type is allowed dbglogging_1.default.dlog('Get request Accept-Encoding does not have gzip nor deflate, but continue...'); } } else { //r3logger.dlog('GET request doe not have Accept-Encoding, but continue...'); } //------------------------------ // get url last path and decode it //------------------------------ // check path matching const requestptn = new RegExp('^/v1/userdata/(.*)'); // regex = /^\/v1\/userdata\/(.*)/ 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 userdata path parameter' }; dbglogging_1.default.elog(result.message); res.type('application/json; charset=utf-8'); k2hr3resutil_1.default.errResponse(req, res, 400, result); // 400: Bad Request return; } // decode and check userdata parameter const udproc = new k2hr3userdata_1.default; const roleinfo = udproc.decryptRoleInfo(reqmatchs[1]); let errorMsg = null; if (!k2hr3apiutil_1.default.isValTypeRoleInfo(roleinfo)) { // [NOTE] // continue for returning error script // errorMsg = 'Get userdata path is invalid.'; dbglogging_1.default.elog(errorMsg); } //------------------------------ // Make response //------------------------------ if (isGzip) { // Gzip const responsebody = udproc.getGzipMultipartUserdata((k2hr3apiutil_1.default.isValTypeRoleInfo(roleinfo) ? roleinfo : {}), errorMsg); const tmp_length = (k2hr3apiutil_1.default.isPlainObject(responsebody) && k2hr3apiutil_1.default.isSafeNumber(responsebody.length)) ? responsebody.length : 0; res.type('application/zip'); res.setHeader('Content-Encoding', 'gzip'); res.setHeader('Content-Transfer-Encoding', 'binary'); res.setHeader('Content-Disposition', 'attachment; filename=k2hr3-userdata.gz'); res.setHeader('Content-Length', tmp_length); dbglogging_1.default.dlog('succeed : (response body is gzip compressed)'); res.status(200); // 200: OK res.send(responsebody?.data ?? ''); } else { // Text const multiobj = udproc.getMultipartUserdata((k2hr3apiutil_1.default.isValTypeRoleInfo(roleinfo) ? roleinfo : {}), errorMsg); const responsebody = multiobj.body; if (k2hr3apiutil_1.default.isSafeString(multiobj.type)) { res.type(multiobj.type); } else { res.type('application/json; charset=utf-8'); } res.setHeader(multiobj.mimeverkey, multiobj.mimeverval); res.setHeader(multiobj.partcntkey, multiobj.partcntval); dbglogging_1.default.dlog('succeed : (response body is not gzip compressed)'); res.status(200); // 200: OK res.send(responsebody); } }); //--------------------------------------------------------- // 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 */