k2hr3-api
Version:
K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules
263 lines (262 loc) • 10.9 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 });
//---------------------------------------------------------
// Imports
//---------------------------------------------------------
const path_1 = __importDefault(require("path"));
const morgan_1 = __importDefault(require("morgan"));
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const body_parser_1 = __importDefault(require("body-parser"));
const k2hr3apiutil_1 = __importDefault(require("./lib/k2hr3apiutil"));
const k2hr3resutil_1 = __importDefault(require("./lib/k2hr3resutil"));
const express_1 = __importDefault(require("express"));
//
// Load Configuration
//
const k2hr3config_1 = require("./lib/k2hr3config");
const apiConf = new k2hr3config_1.r3ApiConfig();
//
// Load variables
//
// - Local Tenants
// - Load CORS(Cross-Origin Resource Sharing) Setting
//
// [NOTE][TODO]
// It specifies a web development machine for temporary debugging.
// In future we plan to specify with K2HR3 role.
//
let is_localtenants = true;
const cors_ips = [];
(() => {
is_localtenants = apiConf.isLocalTenants();
if (k2hr3apiutil_1.default.isSafeEntity(apiConf) && k2hr3apiutil_1.default.isNotEmptyArray(apiConf.getCORSIPs())) {
k2hr3apiutil_1.default.mergeArray(cors_ips, apiConf.getCORSIPs());
}
})();
//---------------------------------------------------------
// Environments
//---------------------------------------------------------
// NODE_ENV(development or production)
// NODE_LOGGER(if 'no', not logging by morgan)
//
const is_product = k2hr3apiutil_1.default.compareCaseString(k2hr3apiutil_1.default.getSafeString(process.env.NODE_ENV), 'production');
const is_logging = !k2hr3apiutil_1.default.compareCaseString(k2hr3apiutil_1.default.getSafeString(process.env.NODE_LOGGER), 'no');
//---------------------------------------------------------
// Routes
//---------------------------------------------------------
const version_1 = __importDefault(require("./routes/version"));
const userTokens_1 = __importDefault(require("./routes/userTokens"));
const policy_1 = __importDefault(require("./routes/policy"));
const resource_1 = __importDefault(require("./routes/resource"));
const role_1 = __importDefault(require("./routes/role"));
const service_1 = __importDefault(require("./routes/service"));
const acr_1 = __importDefault(require("./routes/acr"));
const list_1 = __importDefault(require("./routes/list"));
const userdata_1 = __importDefault(require("./routes/userdata"));
const extdata_1 = __importDefault(require("./routes/extdata"));
const tenant_1 = __importDefault(require("./routes/tenant"));
const debugVerify_1 = __importDefault(require("./routes/debugVerify"));
let tenant = null;
if (is_localtenants) {
tenant = tenant_1.default;
}
let verify = null;
if (!is_product) {
verify = debugVerify_1.default;
}
//
// Express objects
//
const app = (0, express_1.default)();
const userExp = (0, express_1.default)();
const policyExp = (0, express_1.default)();
const resourceExp = (0, express_1.default)();
const roleExp = (0, express_1.default)();
const serviceExp = (0, express_1.default)();
const acrExp = (0, express_1.default)();
const listExp = (0, express_1.default)();
const userdataExp = (0, express_1.default)();
const extdataExp = (0, express_1.default)();
let tenantExp = null;
if (is_localtenants) {
tenantExp = (0, express_1.default)();
}
let verifyExp = null;
if (!is_product) {
verifyExp = (0, express_1.default)();
}
//---------------------------------------------------------
// Trusted proxy
//---------------------------------------------------------
// [NOTE][TODO]
// We set trusted proxy as only loopback now.
// Here, we need to add CDN/Proxy for our NW, but pending now.
//
app.set('trust proxy', 'loopback');
userExp.set('trust proxy', 'loopback');
policyExp.set('trust proxy', 'loopback');
resourceExp.set('trust proxy', 'loopback');
roleExp.set('trust proxy', 'loopback');
serviceExp.set('trust proxy', 'loopback');
acrExp.set('trust proxy', 'loopback');
listExp.set('trust proxy', 'loopback');
userdataExp.set('trust proxy', 'loopback');
extdataExp.set('trust proxy', 'loopback');
if (tenantExp) {
tenantExp.set('trust proxy', 'loopback');
}
if (verifyExp) {
verifyExp.set('trust proxy', 'loopback');
}
//
// CORS(Cross-Origin Resource Sharing) Controller
//
app.use((req, res, next) => {
//
// Do not allow CORS for userToken without tenant name(=put/post unscoped token)
//
const userTokenUrlExp = new RegExp('^/v1/user/tokens(.*)');
if (req.socket.localAddress !== req.socket.remoteAddress && k2hr3apiutil_1.default.isNotEmptyArray(k2hr3apiutil_1.default.getSafeString(req.url).match(userTokenUrlExp))) {
//
// case of POST/PUT userToken
//
if (!k2hr3apiutil_1.default.findStringInArray(apiConf.getCORSIPs(), req.socket.remoteAddress) && !k2hr3apiutil_1.default.findStringInArray(apiConf.getCORSIPs(), '*')) {
// [NOTE]
// If allowcredauth is true in configuration and password is specified on PUT method,
// it allows authorization by credential(username/password).
// This case is used for accessing keystone directly.
// (The password is empty is allowed.)
//
if ((k2hr3apiutil_1.default.compareCaseString(req.method, 'PUT') && k2hr3apiutil_1.default.isSafeEntity(req.query) && k2hr3apiutil_1.default.isSafeString(req.query.username) && !(apiConf.isAllowedCredentialAccess() && k2hr3apiutil_1.default.isSafeEntity(req.query.password))) ||
(k2hr3apiutil_1.default.compareCaseString(req.method, 'POST') && k2hr3apiutil_1.default.isSafeEntity(req.body) && k2hr3apiutil_1.default.isSafeEntity(req.body.auth) && k2hr3apiutil_1.default.isSafeEntity(req.body.auth.passwordCredentials))) {
//
// case of specified user credentials(except specified unscoped token)
//
const result = {
result: false,
message: 'not allow CORS(cross-origin resource sharing) to /v1/user/tokens'
};
k2hr3resutil_1.default.errResponse(req, res, 405, result, 'application/json; charset=utf-8');
return;
}
}
}
//
// Origin is specified, allow it.
//
if (k2hr3apiutil_1.default.isSafeString(req.headers.origin)) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Headers', 'Origin,X-Requested-With,X-HTTP-Method-Override,Content-Type,Accept,X-Auth-Token,x-k2hr3-debug');
res.header('Access-Control-Allow-Methods', 'HEAD,POST,GET,PUT,DELETE,OPTIONS');
res.header('Access-Control-Expose-Headers', 'X-Auth-Token,x-k2hr3-error');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Max-Age', '86400');
}
next();
});
//---------------------------------------------------------
// Express
//---------------------------------------------------------
//
// Setting for express
//
if (is_logging) {
//
// Setup Log
//
const format = apiConf.getAccessLogFormat() ?? 'combined';
const options = apiConf.getMorganLoggerOption(__dirname) ?? undefined;
app.use((0, morgan_1.default)(format, options));
}
app.use(body_parser_1.default.json());
app.use(body_parser_1.default.urlencoded({ extended: false }));
app.use((0, cookie_parser_1.default)());
app.use(express_1.default.static(path_1.default.join(__dirname, 'public')));
app.use('/status.html', express_1.default.static(__dirname + '/public/status.html'));
//
// Route mapping
//
app.use('/', version_1.default); // '/'
app.use('/v1', version_1.default); // '/v1'
userExp.use('/', userTokens_1.default); // '/v1/user/tokens'
policyExp.use('/', policy_1.default); // '/v1/policy'
resourceExp.use('/', resource_1.default); // '/v1/resource'
roleExp.use('/', role_1.default); // '/v1/role'
serviceExp.use('/', service_1.default); // '/v1/service'
acrExp.use('/', acr_1.default); // '/v1/acr'
listExp.use('/', list_1.default); // '/v1/list'
userdataExp.use('/', userdata_1.default); // '/v1/userdata'
extdataExp.use('/', extdata_1.default); // '/v1/extdata'
if (tenantExp && tenant) {
tenantExp.use('/', tenant); // '/v1/tenant'
}
if (verifyExp && verify) {
verifyExp.use('/', verify); // '/v1/debug/verify*'
}
app.use(/^\/v1\/user\/tokens(?:\/.*)?$/, userExp); // mountpath: '/v1/user/tokens*'
app.use(/^\/v1\/policy(?:\/.*)?$/, policyExp); // mountpath: '/v1/policy*'
app.use(/^\/v1\/resource(?:\/.*)?$/, resourceExp); // mountpath: '/v1/resource*'
app.use(/^\/v1\/role(?:\/.*)?$/, roleExp); // mountpath: '/v1/role*'
app.use(/^\/v1\/service(?:\/.*)?$/, serviceExp); // mountpath: '/v1/service*'
app.use(/^\/v1\/acr(?:\/.*)?$/, acrExp); // mountpath: '/v1/acr*'
app.use(/^\/v1\/list(?:\/.*)?$/, listExp); // mountpath: '/v1/list*'
app.use(/^\/v1\/userdata(?:\/.*)?$/, userdataExp); // mountpath: '/v1/userdata*'
app.use(/^\/v1\/extdata(?:\/.*)?$/, extdataExp); // mountpath: '/v1/extdata*'
if (tenantExp) {
app.use(/^\/v1\/tenant(?:\/.*)?$/, tenantExp); // mountpath: '/v1/tenant*'
}
if (verifyExp) {
app.use(/^\/v1\/debug\/verify(?:\/.*)?$/, verifyExp); // mountpath: '/v1/debug/verify*'
}
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
//
// error handler
//
app.use((err, req, res, _next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
const result = {
result: false,
message: 'Internal server error'
};
k2hr3resutil_1.default.errResponse(req, res, (err.status || 500), result, 'application/json; charset=utf-8');
});
//---------------------------------------------------------
// Exports
//---------------------------------------------------------
exports.default = app;
/*
* 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
*/