UNPKG

k2hr3-api

Version:

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

933 lines (932 loc) 38.9 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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.openstackapiv3 = void 0; const https = __importStar(require("https")); const http = __importStar(require("http")); const k2hr3apiutil_1 = __importDefault(require("./k2hr3apiutil")); const dbglogging_1 = __importDefault(require("./dbglogging")); const openstackep_1 = __importDefault(require("./openstackep")); // OpenStack KeyStone EndPoint(=osksep) const cacerts_1 = require("./cacerts"); // // Get Unscoped token by user name and passwd from Openstack identity v3 API // // Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail // // Request: { // "auth": { // "identity": { // "password": { // "user": { // "domain": { // "id": "default" // }, // "password": "*********", // "name": "*********" // } // }, // "methods": ["password"] // } // } // } // // Response: // Header "X-Subject-Token: 16e4638398574f1d9364............" (*1) // Body { // "token": { // "audit_ids": [ // "################....." // ], // "expires_at": "2017-06-17T00:20:38.863072Z", (*2) // "extras": {}, // "issued_at": "2017-06-16T00:20:38.863143Z", // "methods": [ // "password" // ], // "user": { // "domain": { // "id": "default", // "name": "Default" // }, // "id": "*****************************...", (*4) // "name": "user name" (*3) // } // } // } // // callback(error, result): // result = { // user: user name (*3) // userid: user id (*4) // scoped: false (always false) // token: token string(id) (*1) // expire: expire string (*2) // region: region string (region name for keystone endpoint) // token_seed: seed ({publisher: 'OPENSTACKV3'}) // } // const rawGetUserUnscopedTokenV3 = (uname, passwd, callback) => { if (!k2hr3apiutil_1.default.isSafeString(uname)) { const error = new Error('some parameters are wrong : uname=' + JSON.stringify(uname)); dbglogging_1.default.elog(error.message); callback(error, null); return; } if (!k2hr3apiutil_1.default.isSafeString(passwd)) { passwd = null; } const _uname = uname; const _passwd = passwd; const _callback = callback; // get end points for keystone openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => { if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) { const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : '')); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // got safe endpoint for keystone //r3logger.dlog(keystone_ep); // build parameters for request const body = { 'auth': { 'identity': { 'password': { 'user': { 'domain': { 'id': 'default' }, 'password': _passwd, 'name': _uname } }, 'methods': ['password'] } } }; const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http; const strbody = JSON.stringify(body); const headers = { 'Content-Type': 'application/json', 'Content-Length': strbody.length }; const options = { 'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname), 'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0, 'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens', 'method': 'POST', 'headers': headers, 'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined }; // send request const req = agent.request(options, (res) => { let body = ''; const status = res.statusCode; const headers = res.headers; dbglogging_1.default.dlog('response status: ' + res.statusCode); dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', (chunk) => { //r3logger.dlog('response chunk: ' + chunk); body += chunk; }); res.on('end', () => { if (300 <= (status ?? 500)) { const error = new Error('could not get unscoped token by status=' + String(status ?? 500)); dbglogging_1.default.elog(error.message); _callback(error, null); return; } if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) { const error = new Error('could not find unscoped token in header(X-Subject-Token)'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']); //r3logger.dlog('response body: ' + body); let res_body = body; if (k2hr3apiutil_1.default.checkSimpleJSON(body)) { res_body = JSON.parse(body); } if (!k2hr3apiutil_1.default.isPlainObject(res_body) || !k2hr3apiutil_1.default.isPlainObject(res_body.token) || !k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) || !k2hr3apiutil_1.default.isPlainObject(res_body.token.user) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.name) || !k2hr3apiutil_1.default.compareCaseString(res_body.token.user.name, _uname)) { const error = new Error('could not get unscoped token by something wrong response body : ' + body); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // convert openstack user id(16 bytes hex string) to UUID(not UUID4) const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16)); // build result const resobj = { user: res_body.token.user.name.toLowerCase(), userid: user_id_uuid4, scoped: false, token: tmpToken, expire: k2hr3apiutil_1.default.getSafeString(res_body.token.expires_at), region: k2hr3apiutil_1.default.getSafeString(keystone_ep.region).toLowerCase(), token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' }) }; _callback(null, resobj); return; }); }); req.on('error', (exception) => { dbglogging_1.default.elog('problem with request: ' + exception.message); _callback(exception, null); return; }); // write data to request body req.write(strbody); req.end(); }, true); }; // // Get Unscoped token by openstack token from Openstack identity v3 API // // Document: https://docs.openstack.org/api-ref/identity/v3/?expanded=token-authentication-with-unscoped-authorization-detail#token-authentication-with-unscoped-authorization // // Request: { // "auth": { // "identity": { // "methods": [ // "token" // ], // "token": { // "id": "**********" // } // } // } // } // // // Response: // Header "X-Subject-Token: 16e4638398574f1d9364............" (*1) // { // "token": { // "audit_ids": [ // "################....." // ], // "expires_at": "2017-06-17T00:20:38.863072Z", (*2) // "issued_at": "2015-11-05T21:00:33.819948Z", // "methods": [ // "token" // ], // "user": { // "domain": { // "id": "default", // "name": "Default" // }, // "id": "*****************************...", (*4) // "name": "user name" (*3) // "password_expires_at": null // } // } // } // // callback(error, result): // result = { // user: user name (*3) // userid: user id (*4) // scoped: false (always false) // token: token string(id) (*1) // expire: expire string (*2) // region: region string (region name for keystone endpoint) // token_seed: seed ({publisher: 'OPENSTACKV3'}) // } // const rawGetUserUnscopedTokenByOstokenV3 = (ostoken, callback) => { if (!k2hr3apiutil_1.default.isSafeString(ostoken)) { const error = new Error('some parameter is wrong : ostoken=' + JSON.stringify(ostoken)); dbglogging_1.default.elog(error.message); callback(error, null); return; } const _ostoken = ostoken; const _callback = callback; // get end points for keystone openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => { if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) { const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : '')); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // got safe endpoint for keystone //r3logger.dlog(keystone_ep); // build parameters for request const body = { 'auth': { 'identity': { 'token': { 'id': _ostoken, }, 'methods': ['token'] } } }; const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http; const strbody = JSON.stringify(body); const headers = { 'Content-Type': 'application/json', 'Content-Length': strbody.length }; const options = { 'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname), 'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0, 'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens', 'method': 'POST', 'headers': headers, 'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined }; // send request const req = agent.request(options, (res) => { let body = ''; const status = res.statusCode; const headers = res.headers; dbglogging_1.default.dlog('response status: ' + res.statusCode); dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', (chunk) => { //r3logger.dlog('response chunk: ' + chunk); body += chunk; }); res.on('end', () => { if (300 <= (status ?? 500)) { const error = new Error('could not get unscoped token by status=' + String(status ?? 500)); dbglogging_1.default.elog(error.message); _callback(error, null); return; } if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) { const error = new Error('could not find unscoped token in header(X-Subject-Token)'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']); //r3logger.dlog('response body: ' + body); let res_body = body; if (k2hr3apiutil_1.default.checkSimpleJSON(body)) { res_body = JSON.parse(body); } if (!k2hr3apiutil_1.default.isPlainObject(res_body) || !k2hr3apiutil_1.default.isPlainObject(res_body.token) || !k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) || !k2hr3apiutil_1.default.isPlainObject(res_body.token.user) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.name)) { const error = new Error('could not get unscoped token by something wrong response body : ' + body); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // convert openstack user id(16 bytes hex string) to UUID(not UUID4) const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16)); // build result const resobj = { user: res_body.token.user.name.toLowerCase(), userid: user_id_uuid4, scoped: false, token: tmpToken, expire: k2hr3apiutil_1.default.getSafeString(res_body.token.expires_at), region: k2hr3apiutil_1.default.getSafeString(keystone_ep.region).toLowerCase(), token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' }) }; _callback(null, resobj); }); }); req.on('error', (exception) => { dbglogging_1.default.elog('problem with request: ' + exception.message); _callback(exception, null); return; }); // write data to request body req.write(strbody); req.end(); }, true); }; // // Get Scoped token by unscoped token and tenant name from Openstack identity v3 API // // Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=token-authentication-with-scoped-authorization-detail // // Request: { // "auth": { // "identity": { // "methods": [ // "token" // ], // "token": { // "id": "<unscoped token>" // } // }, // "scope": { // "project": { // "id": "<project(tenant) id>" // } // } // } // } // // Response: // Header "X-Subject-Token: 16e4638398574f1d9364............" (*1) // Body { // "token": { // "audit_ids": [ // "######################", // "######################" // ], // "catalog": [ // { // "endpoints": [ // { // "id": "*****************************...", // "interface": "public" (=public, others are admin/internal), // "region": "***************", (*5) // "region_id": ############### // "url": "http://keystone/v2.0" // }, // ... // ], // "id": "*****************************...", // "name": "keystone", // "type": "identity" (=identity) // }, // ... // ], // "expires_at": "2017-06-17T00:46:16.923305Z", (*2) // "extras": {}, // "issued_at": "2017-06-16T01:07:56.325351Z", // "methods": [ // "token", // "password" // ], // "project": { // "domain": { // "id": "default", // "name": "Default" // }, // "id": "*****************************...", (=project id) // "name": "project name" (*5: project(tenant) name) // }, // "roles": [ // { // "id": "*****************************...", // "name": "_member_" // } // ], // "user": { // "domain": { // "id": "default", // "name": "Default" // }, // "id": "*****************************...", (*4: user id) // "name": "user name" (*3: user name) // } // } // } // // callback(error, result): // result = { // user: user name (*3) // userid: user id (*4) // scoped: true (always true) // token: token string(id) (*1) // expire: expire string (*2) // region: region string (identity's *5 for region name) // token_seed: seed ({publisher: 'OPENSTACKV3'}) // } // const rawGetUserScopedTokenV3 = (unscopedtoken, tenantid, callback) => { if (!k2hr3apiutil_1.default.isSafeString(unscopedtoken)) { const error = new Error('parameter is wrong : unscopedtoken=' + JSON.stringify(unscopedtoken)); dbglogging_1.default.elog(error.message); callback(error, null); return; } if (!k2hr3apiutil_1.default.isSafeNumeric(tenantid)) { // id allows hex character string, decimal character string, decimal number value. // const error = new Error('parameter is wrong : tenantid=' + JSON.stringify(tenantid)); dbglogging_1.default.elog(error.message); callback(error, null); return; } if (!k2hr3apiutil_1.default.isSafeString(tenantid)) { // to string tenantid = String(tenantid); } const _unscopedtoken = unscopedtoken; const _tenantid = tenantid; const _callback = callback; // get end points for keystone openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => { if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) { const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : '')); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // got safe endpoint for keystone //r3logger.dlog(keystone_ep); // build parameters for request const body = { 'auth': { 'identity': { 'methods': [ 'token' ], 'token': { 'id': _unscopedtoken } }, 'scope': { 'project': { 'id': _tenantid } } } }; const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http; const strbody = JSON.stringify(body); const headers = { 'Content-Type': 'application/json', 'Content-Length': strbody.length }; const options = { 'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname), 'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0, 'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens', 'method': 'POST', 'headers': headers, 'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined }; // send request const req = agent.request(options, (res) => { let body = ''; const status = res.statusCode; const headers = res.headers; dbglogging_1.default.dlog('response status: ' + res.statusCode); dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', (chunk) => { //r3logger.dlog('response chunk: ' + chunk); body += chunk; }); res.on('end', () => { if (300 <= (status ?? 500)) { const error = new Error('could not get scoped token by status=' + String(status ?? 500)); dbglogging_1.default.elog(error.message); _callback(error, null); return; } if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) { const error = new Error('could not find unscoped token in header(X-Subject-Token)'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']); //r3logger.dlog('response body: ' + body); let res_body = body; if (k2hr3apiutil_1.default.checkSimpleJSON(body)) { res_body = JSON.parse(body); } if (!k2hr3apiutil_1.default.isPlainObject(res_body) || !k2hr3apiutil_1.default.isPlainObject(res_body.token) || !k2hr3apiutil_1.default.isNotEmptyArray(res_body.token.catalog) || !k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) || !k2hr3apiutil_1.default.isPlainObject(res_body.token.project) || !k2hr3apiutil_1.default.isSafeString(res_body.token.project.id) || !k2hr3apiutil_1.default.compareCaseString(res_body.token.project.id, _tenantid) || !k2hr3apiutil_1.default.isSafeString(res_body.token.project.name) || !k2hr3apiutil_1.default.isPlainObject(res_body.token.user) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) || !k2hr3apiutil_1.default.isSafeString(res_body.token.user.name)) { const error = new Error('could not get scoped token by something wrong response body : ' + body); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // check & get region let region = null; for (let cnt = 0; cnt < res_body.token.catalog.length && null === region; ++cnt) { const tmpCatalog = res_body.token.catalog[cnt]; if (!k2hr3apiutil_1.default.isPlainObject(tmpCatalog) || !k2hr3apiutil_1.default.isNotEmptyArray(tmpCatalog.endpoints) || !k2hr3apiutil_1.default.isSafeString(tmpCatalog.type)) { dbglogging_1.default.wlog('one of response for scoped token is something wrong : ' + JSON.stringify(tmpCatalog)); continue; } // target region by type=identity if (!k2hr3apiutil_1.default.compareCaseString('identity', tmpCatalog.type)) { continue; } // check region for (let cnt2 = 0; cnt2 < tmpCatalog.endpoints.length; ++cnt2) { const tmpEp = tmpCatalog.endpoints[cnt2]; if (k2hr3apiutil_1.default.isPlainObject(tmpEp) && k2hr3apiutil_1.default.isSafeString(tmpEp.interface) && k2hr3apiutil_1.default.compareCaseString('public', tmpEp.interface) && k2hr3apiutil_1.default.isSafeString(tmpEp.region)) { if (k2hr3apiutil_1.default.compareCaseString(tmpEp.region, keystone_ep.region)) { // found region = tmpEp.region; break; } else { dbglogging_1.default.wlog('unknown region(' + tmpEp.region + '), we need to find region(' + keystone_ep.region + '), so skip this'); } } else { dbglogging_1.default.dlog('one of response endpoint for scoped token is not target or something wrong : ' + JSON.stringify(tmpEp)); } } } if (!k2hr3apiutil_1.default.isSafeString(region)) { const error = new Error('could not find request region in result.'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // convert openstack user id(16 bytes hex string) to UUID(not UUID4) const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16)); // build result const resobj = { user: res_body.token.user.name.toLowerCase(), userid: user_id_uuid4, scoped: false, token: tmpToken, expire: res_body.token.expires_at, region: region.toLowerCase(), token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' }) }; _callback(null, resobj); }); }); req.on('error', (exception) => { dbglogging_1.default.elog('problem with request: ' + exception.message); _callback(exception, null); return; }); // write data to request body req.write(strbody); req.end(); }, true); }; // // Verify User Token for OpenStack V3 // // user : target user name for token // token : check token // token_seed : token seed data // // result : { // result: true/false // message: null or error message string // } // const rawVerifyUserTokenPublisherV3 = (token_seed) => { if (!k2hr3apiutil_1.default.isSafeString(token_seed)) { return false; } // parse seed if (!k2hr3apiutil_1.default.checkSimpleJSON(token_seed)) { return false; } const seed = k2hr3apiutil_1.default.parseJSON(token_seed); if (!k2hr3apiutil_1.default.isValTypeTokenSeed(seed)) { return false; } if (!k2hr3apiutil_1.default.isSafeString(seed.publisher) || (seed.publisher != 'OPENSTACKV3')) // publisher must be 'OPENSTACKV3' { return false; } return true; }; const rawWrapVerifyUserTokenPublisherV3 = (token_seed) => { const resobj = { result: true, message: null }; if (!rawVerifyUserTokenPublisherV3(token_seed)) { resobj.result = false; resobj.message = 'token_seed(not printable) is not safe entity.'; dbglogging_1.default.elog(resobj.message); return resobj; } return resobj; }; const rawVerifyUserTokenV3 = (user, token, token_seed) => { const resobj = { result: true, message: null }; if (!k2hr3apiutil_1.default.isSafeString(user) || !k2hr3apiutil_1.default.isSafeString(token) || !k2hr3apiutil_1.default.isSafeString(token_seed)) { resobj.result = false; resobj.message = 'some parameters are wrong : token=' + JSON.stringify(token) + ', token_seed=<not printable>, user=' + JSON.stringify(user); dbglogging_1.default.elog(resobj.message); return resobj; } // check seed if (!rawVerifyUserTokenPublisherV3(token_seed)) { resobj.result = false; resobj.message = 'token_seed(not printable) is not safe entity.'; dbglogging_1.default.elog(resobj.message); return resobj; } return resobj; }; // // Get tenant list by unscoped token from Openstack identity v3 API // // Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=list-projects-detail // // Request: X-Auth-Token: <unscoped token> // https://keystone/v3/users/<user id>/projects // // Response: { // "links": { // "next": null, // "previous": null, // "self": "https://keystone/v3/users/<user id>/projects" // }, // "projects": [ // { // "description": null, (*1) // "domain_id": "default", // "enabled": true, // "id": "<project id>", (*2) // "is_domain": false, // "links": { // "self": "https://keystone/v3/projects/<project id>" // }, // "name": "******", (*3) // "parent_id": null // }, // ... // ] // } // // callback(error, result): // result = [ // { // name: project(tenant) name (*3) // id: project(tenant) id (*2) // description: project(tenant) description (*1) // display: display name (*3) // }, // ... // ] // // [TODO] // Should not we use "tenant id" instead of "tenant name" for "name"? // We need this consideration. // const rawGetUserTenantListV3 = (unscopedtoken, userid, callback) => { if (!k2hr3apiutil_1.default.isSafeString(unscopedtoken) || !k2hr3apiutil_1.default.isSafeString(userid)) { const error = new Error('parameter is wrong : unscopedtoken=' + JSON.stringify(unscopedtoken) + ', userid=' + JSON.stringify(userid)); dbglogging_1.default.elog(error.message); callback(error, null); return; } const _unscopedtoken = unscopedtoken; const _callback = callback; // convert user id(UUID) to openstack user id(16 bytes hex string) const _bin_userid = k2hr3apiutil_1.default.cvtStrToBinUuid4(userid); if (null == _bin_userid) { const error = new Error('parameter is wrong : userid=' + JSON.stringify(userid)); dbglogging_1.default.elog(error.message); callback(error, null); return; } const _userid = _bin_userid.toString('hex'); // get end points for keystone openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => { if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) { const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : '')); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // got safe endpoint for keystone //r3logger.dlog(keystone_ep); // build parameters for request const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http; const headers = { 'Content-Type': 'application/json', 'X-Auth-Token': _unscopedtoken, 'Content-Length': 0 }; const options = { 'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname), 'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0, 'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/users/' + _userid + '/projects', 'method': 'GET', 'headers': headers, 'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined }; // send request const req = agent.request(options, (res) => { let body = ''; const status = res.statusCode; dbglogging_1.default.dlog('response status: ' + res.statusCode); dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', (chunk) => { //r3logger.dlog('response chunk: ' + chunk); body += chunk; }); res.on('end', () => { if (300 <= (status ?? 500)) { const error = new Error('could not get scoped token by status=' + String(status ?? 500)); dbglogging_1.default.elog(error.message); _callback(error, null); return; } //r3logger.dlog('response body: ' + body); let res_body = body; if (k2hr3apiutil_1.default.checkSimpleJSON(body)) { res_body = JSON.parse(body); } if (!k2hr3apiutil_1.default.isPlainObject(res_body) || !k2hr3apiutil_1.default.isNotEmptyArray(res_body.projects)) { const error = new Error('failed to get project(tenant) list by unscoped token.'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } // convert result array const resobj = []; for (let cnt = 0; cnt < res_body.projects.length; ++cnt) { const tmpProject = res_body.projects[cnt]; if (!k2hr3apiutil_1.default.isPlainObject(tmpProject) || !k2hr3apiutil_1.default.isSafeString(tmpProject.id) || !k2hr3apiutil_1.default.isSafeString(tmpProject.name)) { dbglogging_1.default.wlog('one of response for project(tenant) list is something wrong : ' + JSON.stringify(tmpProject)); continue; } const tenant = { name: tmpProject.name.toLowerCase(), id: tmpProject.id.toLowerCase(), description: k2hr3apiutil_1.default.getSafeString(tmpProject.description), display: tmpProject.name }; resobj.push(tenant); } if (0 === resobj.length) { const error = new Error('could not get any projects(tenant) list by unscoped token.'); dbglogging_1.default.elog(error.message); _callback(error, null); return; } _callback(null, resobj); }); }); req.on('error', (exception) => { dbglogging_1.default.elog('problem with request: ' + exception.message); callback(exception, null); return; }); }, true); }; //--------------------------------------------------------- // Exports //--------------------------------------------------------- exports.openstackapiv3 = { getUserUnscopedToken: rawGetUserUnscopedTokenV3, // // token : unscoped/scoped token for openstack // getUserUnscopedTokenByToken: rawGetUserUnscopedTokenByOstokenV3, // // tenantname : not used // tenantid : for keystone v3 api // getUserScopedToken: rawGetUserScopedTokenV3, // // Verify seed publisher type // verifyUserTokenPublisher: rawWrapVerifyUserTokenPublisherV3, // // Verify token // // tenant is not used. // verifyUserToken: (dkcobj_permanent, user, tenant, token, token_seed) => { return rawVerifyUserTokenV3(user, token, token_seed); }, // // userid : for keystone v3 api // getUserTenantList: rawGetUserTenantListV3 }; // // Default // exports.default = exports.openstackapiv3; /* * 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 */