k2hr3-api
Version:
K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules
933 lines (932 loc) • 38.9 kB
JavaScript
;
/*
* 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
*/