UNPKG

k2hr3-api

Version:

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

483 lines (482 loc) 20.1 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 dbglogging_1 = __importDefault(require("../lib/dbglogging")); const express_1 = __importDefault(require("express")); const router = express_1.default.Router(); // // Common utility function // const rawCommonGetUserToken = (req, res, unscopedToken, otherToken, username, passwd, tenant) => { // arguments const _req = req; const _res = res; const _unscopedToken = k2hr3apiutil_1.default.getSafeString(unscopedToken); const _otherToken = k2hr3apiutil_1.default.getSafeString(otherToken); const _username = k2hr3apiutil_1.default.getSafeString(username); const _passwd = k2hr3apiutil_1.default.getSafeString(passwd); const _tenant = k2hr3apiutil_1.default.getSafeString(tenant); if (!k2hr3apiutil_1.default.isSafeString(_unscopedToken) && !k2hr3apiutil_1.default.isSafeString(_otherToken)) { // // Get token from User Credentials // if (!k2hr3apiutil_1.default.isSafeString(_username)) { const error = { result: false, message: 'Some parameter(user name or unscoped token) is wrong.' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } k2hr3tokens_1.default.getUserToken(_username, _passwd, _tenant, (err, token) => { if (null !== err || null === token) { const error = { result: false, message: 'could not get scoped user token for user=' + _username + ', tenant=' + _tenant + ' by ' + (err?.message ?? '') }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(_req, _res, 404, error); // 404: Not Found return; } dbglogging_1.default.dlog('get user token jsonres = ' + JSON.stringify(token)); const result = { result: true, message: 'succeed', scoped: k2hr3apiutil_1.default.isSafeString(_tenant), token: token }; _res.status(201); // 201: Created _res.send(JSON.stringify(result)); }); } else if (k2hr3apiutil_1.default.isSafeString(_unscopedToken)) { // // Get Scoped token from Unscoped token // if (!k2hr3apiutil_1.default.isSafeString(_username)) { const error = { result: false, message: 'Some parameter(user name or unscoped token) is wrong.' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } k2hr3tokens_1.default.getScopedUserToken(_unscopedToken, _username, _tenant, (err, token) => { if (null !== err || null === token) { const error = { result: false, message: 'could not get scoped user token for user=' + _username + ', tenant=' + _tenant + ' by ' + (err?.message ?? '') }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(_req, _res, 404, error); // 404: Not Found return; } dbglogging_1.default.dlog('get user token jsonres = ' + JSON.stringify(token)); const result = { result: true, message: 'succeed', scoped: k2hr3apiutil_1.default.isSafeString(_tenant), token: token }; _res.status(201); // 201: Created _res.send(JSON.stringify(result)); }); } else if (k2hr3apiutil_1.default.isSafeString(_otherToken)) { // // Get Scoped/Unscoped token from other token // k2hr3tokens_1.default.getUserTokenByToken(_otherToken, _tenant, (err, token) => { if (null !== err || null === token) { const error = { result: false, message: 'could not get scoped user token for other token, tenant=' + _tenant + ' by ' + (err?.message ?? '') }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(_req, _res, 404, error); // 404: Not Found return; } dbglogging_1.default.dlog('get user token jsonres = ' + JSON.stringify(token)); const result = { result: true, message: 'succeed', scoped: k2hr3apiutil_1.default.isSafeString(_tenant), token: token }; _res.status(201); // 201: Created _res.send(JSON.stringify(result)); }); } }; // // Common utility function // const rawGetUnscopedUserToken = (req) => { // check unscoped token in request const resobj = k2hr3tokens_1.default.checkToken(req, false, true); if (!resobj.result) { const result = { result: resobj.result, message: k2hr3apiutil_1.default.getSafeString(resobj.message), status: resobj.status, token: '' }; return result; } const token_info = resobj.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { const result = { result: false, message: 'could not get unscoped user token in request.', status: 400, // 400: Bad Request token: '' }; return result; } const func_result = { result: true, message: '', status: 200, token: k2hr3apiutil_1.default.getSafeString(resobj.token), username: k2hr3apiutil_1.default.getSafeString(token_info.user) }; return func_result; }; // Mountpath : '/v1/user/tokens' // POST '/v1/user/tokens' : post(create) user token on version 1 // response body : result => true/false // message => messages // scoped => true/false // token => token(unscoped or scoped) // 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)) { const error = { result: false, message: 'POST body does not have auth key' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } // arguments const tenant = (k2hr3apiutil_1.default.isPlainObject(req.body.auth) && k2hr3apiutil_1.default.isSafeString(req.body.auth.tenantName)) ? k2hr3apiutil_1.default.getSafeString(req.body.auth.tenantName) : null; let unscopedtoken = null; let otherToken = null; let username = null; let passwd = null; if (!k2hr3apiutil_1.default.isPlainObject(req.body.auth) || !k2hr3apiutil_1.default.isPlainObject(req.body.auth.passwordCredentials)) { // // Token is required if no user credentials are specified. // // [NOTE] // There are two cases in this case: // (1) Specify the UnscopedToken registered in k2hr3 to get the ScopedToken(must specify the tenant name) // (2) Specify a token other than k2hr3 (OpenStack, etc.) and perform Unauthenticated Token after user authentication. // In this case, if tenant is specified, ScopedToken can be obtained directly. // // get unscoped token const resobj = rawGetUnscopedUserToken(req); if (resobj.result) { // // (1) case of unscoped token registered in k2hr3 // if (!k2hr3apiutil_1.default.isPlainObject(req.body.auth) || !k2hr3apiutil_1.default.isSafeString(req.body.auth.tenantName)) { const error = { result: false, message: 'POST body does not have tenant name(or user credentials)' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } username = k2hr3apiutil_1.default.getSafeString(resobj.username); unscopedtoken = resobj.token; } else { // // (2) get (un)scoped token from other a token other than k2hr3(OpenStack, etc.) // otherToken = k2hr3tokens_1.default.getAuthTokenHeader(req, false); if (!k2hr3apiutil_1.default.isSafeString(otherToken)) { const error = { result: false, message: resobj.message }; dbglogging_1.default.elog(resobj.message); k2hr3resutil_1.default.errResponse(req, res, resobj.status, error); // 40X return; } } } else { // // case of user credentials // username = k2hr3apiutil_1.default.getSafeString(req.body.auth.passwordCredentials.username); passwd = k2hr3apiutil_1.default.getSafeString(req.body.auth.passwordCredentials.password); // password is allowed empty, it depends on the authentication system. } return rawCommonGetUserToken(req, res, unscopedtoken, otherToken, username, passwd, tenant); }); // Mountpath : '/v1/user/tokens' // PUT '/v1/user/tokens' : put(create) user token on version 1 // response body : result => true/false // message => messages // scoped => true/false // token => token(unscoped or scoped) // 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 error = { result: false, message: 'PUT argument does not have any data' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } // arguments const tenant = k2hr3apiutil_1.default.getSafeString(req.query.tenantname); let unscopedtoken = null; let otherToken = null; let username = null; let passwd = null; if (!k2hr3apiutil_1.default.isSafeString(req.query.username)) { // // Token is required if no user credentials are specified. // // [NOTE] // There are two cases in this case: // (1) Specify the UnscopedToken registered in k2hr3 to get the ScopedToken(must specify the tenant name) // (2) Specify a token other than k2hr3 (OpenStack, etc.) and perform Unauthenticated Token after user authentication. // In this case, if tenant is specified, ScopedToken can be obtained directly. // // get unscoped token const resobj = rawGetUnscopedUserToken(req); if (resobj.result) { // // (1) case of unscoped token registered in k2hr3 // if (!k2hr3apiutil_1.default.isSafeString(req.query.tenantname)) { const error = { result: false, message: 'POST body does not have tenant name(or user credentials)' }; dbglogging_1.default.elog(error.message); k2hr3resutil_1.default.errResponse(req, res, 400, error); // 400: Bad Request return; } username = k2hr3apiutil_1.default.getSafeString(resobj.username); unscopedtoken = resobj.token; } else { // // (2) get (un)scoped token from other a token other than k2hr3(OpenStack, etc.) // otherToken = k2hr3tokens_1.default.getAuthTokenHeader(req, false); if (!k2hr3apiutil_1.default.isSafeString(otherToken)) { const error = { result: false, message: resobj.message }; dbglogging_1.default.elog(resobj.message); k2hr3resutil_1.default.errResponse(req, res, resobj.status, error); // 40X return; } } } else { // // case of user credentials // username = k2hr3apiutil_1.default.getSafeString(req.query.username); passwd = k2hr3apiutil_1.default.isSafeEntity(req.query.password) ? decodeURIComponent(k2hr3apiutil_1.default.getSafeString(req.query.password)) : null; // password is allowed empty, it depends on the authentication system. } return rawCommonGetUserToken(req, res, unscopedtoken, otherToken, username, passwd, tenant); }); // // Mountpath : '/v1/user/tokens' // // GET '/v1/user/tokens' : get user token on version 1 // response body : result => true/false // message => messages // scoped => true/false // user => user name // tenants => [ // { // name: "tenant name" // display: "display name" // id: "tenant id" // description: "tenant description" // }, // ... // ] // // [NOTE] // If token is scoped, tenants array has only 1 element. // Which element has name and display member, but display is as same as name. // It is not real display name, because we take a cost getting it from APIs. // router.get('/', (req, res, next) => { dbglogging_1.default.dlog('CALL:', req.method, req.url); if ('HEAD' === req.method) { // HEAD request comes here, so it should be routed to head function. next(); return; } const _res = res; const _req = req; _res.type('application/json; charset=utf-8'); //------------------------------ // get token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(_req, false, true); // not scope check, 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; } // build response body if (token_info.scoped) { // scoped token const tenant_info = { name: k2hr3apiutil_1.default.getSafeString(token_info.tenant), display: k2hr3apiutil_1.default.isSafeString(token_info.display) ? token_info.display : null, id: k2hr3apiutil_1.default.getSafeString(token_info.id), description: k2hr3apiutil_1.default.isSafeString(token_info.description) ? token_info.description : null }; const result = { result: true, message: 'succeed', scoped: true, user: k2hr3apiutil_1.default.getSafeString(token_info.user), tenants: [tenant_info] }; _res.status(200); // 200: OK _res.send(JSON.stringify(result)); } else { // check and initialize tenant list k2hr3tokens_1.default.initializeTenantList((k2hr3apiutil_1.default.isSafeString(token_result.token) ? token_result.token : null), token_info.user, (err, tenant_list) => { if (null !== err || null === tenant_list) { const result = { result: false, message: 'failed to get tenant list for user (' + token_info.user + ') by unscoped token(' + token_result.token + ') : ' + (err?.message ?? '') }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(_req, _res, 404, result); // 404: Not Found return; } // reget tenant list const tenant_info_list = k2hr3tokens_1.default.getTenantList(token_info.user); if (!k2hr3apiutil_1.default.isArray(tenant_info_list) || !k2hr3apiutil_1.default.isNotEmptyArray(tenant_info_list)) { const result = { result: false, message: 'token(' + token_result.token + ') for user (' + token_info.user + ') does not have any tenant.' }; dbglogging_1.default.elog(result.message); k2hr3resutil_1.default.errResponse(_req, _res, 404, result); // 404: Not Found return; } const resobj = { result: true, message: 'succeed', scoped: false, user: k2hr3apiutil_1.default.getSafeString(token_info.user), tenants: tenant_info_list }; _res.status(200); // 200: OK _res.send(JSON.stringify(resobj)); }); } }); // Mountpath : '/v1/user/tokens' // HEAD '/v1/user/tokens' : check user token on version 1 // response body : no // router.head('/', (req, res, _) => { dbglogging_1.default.dlog('CALL:', req.method, req.url); const _res = res; const _req = req; _res.type('application/json; charset=utf-8'); //------------------------------ // get token //------------------------------ const token_result = k2hr3tokens_1.default.checkToken(_req, false, true); // not scope check, user token if (!token_result.result) { dbglogging_1.default.elog(token_result.message); k2hr3resutil_1.default.errResponse(_req, _res, token_result.status); return; } const token_info = token_result.token_info; if (!k2hr3tokens_1.default.isResTypeCheckRoleToken(token_info)) { dbglogging_1.default.elog('specified wrong token or it is not scoped user token'); k2hr3resutil_1.default.errResponse(_req, _res, 400); // 400: Bad Request return; } // token is not expired and it is safe. dbglogging_1.default.mlog(dbglogging_1.default.dump(token_info)); _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 */