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