k2hr3-api
Version:
K2HR3 REST API is K2hdkc based Resource and Roles and policy Rules
1,445 lines (1,444 loc) • 46.3 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 __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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.k2hr3ppiutil = void 0;
const dns = __importStar(require("dns"));
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
const url = __importStar(require("url"));
//---------------------------------------------------------
// Utilities for variables
//---------------------------------------------------------
const rawIsSafeEntity = (data) => {
return (undefined !== data && null !== data);
};
const rawIsString = (str) => {
return (undefined !== str && null !== str && 'string' === typeof str);
};
const rawIsSafeString = (str) => {
return (undefined !== str && null !== str && 'string' === typeof str && 0 < str.length);
};
const rawIsPlainObject = (data) => {
return (undefined !== data && null !== data && 'object' === typeof data && !Array.isArray(data));
};
const rawIsValTypeAllObject = (val) => {
if (!rawIsPlainObject(val)) {
return false;
}
for (const [, value] of Object.entries(val)) {
if (!rawIsValTypeAll(value)) {
return false;
}
}
return true;
};
const rawIsValTypeAll = (val) => {
if (null === val) {
return true;
}
else if (rawIsBoolean(val) || rawIsSafeNumber(val) || rawIsString(val)) {
return true;
}
else if (rawIsArray(val)) {
return val.every((element) => rawIsValTypeAll(element));
}
else if ('object' === typeof val) {
return rawIsValTypeAllObject(val);
}
else {
return false;
}
};
const rawIsFunction = (val) => {
return (undefined !== val && null !== val && 'function' === typeof val);
};
const rawIsBoolean = (val) => {
return (undefined !== val && null !== val && 'boolean' === typeof val);
};
const rawIsSafeNumber = (num) => {
return (undefined !== num && null !== num && 'number' === typeof num && Number.isFinite(num)); // except NaN and Infinity
};
const numericStringRegex = /^[+-]?(?:\d+|\d*\.\d+)(?:[eE][+-]?\d+)?$/;
const rawIsSafeNumeric = (strnum) => {
if (rawIsSafeNumber(strnum)) {
return true;
}
else if (rawIsSafeString(strnum)) {
const tmp = strnum.trim();
if (0 === tmp.length || !numericStringRegex.test(tmp)) {
return false;
}
return Number.isFinite(Number(tmp));
}
return false;
};
const rawCvtToNumber = (num) => {
if (rawIsSafeNumber(num)) {
return num;
}
else if (rawIsSafeString(num) && rawIsSafeNumeric(num)) {
return Number(num.trim());
}
return null;
};
const rawCompareCaseString = (str1, str2) => {
if (rawIsSafeString(str1) && rawIsSafeString(str2) && str1.toLowerCase() === str2.toLowerCase()) {
return true;
}
return false;
};
const rawIsArray = (arr) => {
return Array.isArray(arr);
};
const rawIsReadonlyArray = (arr) => {
return Array.isArray(arr);
};
const rawIsStringArray = (arr) => {
return (Array.isArray(arr) && arr.every((element) => rawIsString(element)));
};
const rawIsString2DArray = (arr) => {
return rawIsArray(arr) && arr.every((row) => rawIsArray(row) && row.every((it) => rawIsString(it)));
};
const rawHasPartString = (strbase, sep, values, iscase) => {
if (!rawIsSafeString(strbase) || !rawIsSafeString(sep)) {
return false;
}
let valarray = [];
if (rawIsSafeString(values)) {
valarray.push(values);
}
else if (rawIsArray(values)) {
valarray = values;
}
else {
return false;
}
if (!rawIsBoolean(iscase)) {
iscase = true;
}
const basearray = strbase.split(sep);
for (let cnt1 = 0; cnt1 < basearray.length; ++cnt1) {
const strtmp1 = basearray[cnt1].trim();
if (!rawIsSafeString(strtmp1)) {
continue;
}
for (let cnt2 = 0; cnt2 < valarray.length; ++cnt2) {
const strtmp2 = valarray[cnt2].trim();
if (!rawIsSafeString(strtmp2)) {
continue;
}
if (iscase) {
if (rawCompareCaseString(strtmp1, strtmp2)) {
return true;
}
}
else {
if (strtmp1 === strtmp2) {
return true;
}
}
}
}
return false;
};
const rawGetSafeString = (str) => {
if (rawIsSafeString(str)) {
return str;
}
return '';
};
const rawCheckSimpleJSON = (str) => {
if (!rawIsString(str)) {
return false;
}
try {
JSON.parse(str);
return true;
}
catch {
return false;
}
};
const rawParseJSON = (str) => {
if (!rawIsString(str)) {
return null;
}
try {
return JSON.parse(str);
}
catch {
return null;
}
};
const rawIsNotEmptyArray = (arr) => {
return (Array.isArray(arr) && 0 !== arr.length);
};
const rawGetSafeArray = (arr) => {
return Array.isArray(arr) ? arr.slice() : [];
};
const rawGetSafeStringArray = (arr) => {
if (rawIsSafeString(arr)) {
return [arr];
}
else if (rawIsStringArray(arr)) {
return arr.slice();
}
return [];
};
const rawFindStringInArray = (arr, str) => {
if (!rawIsReadonlyArray(arr)) {
return false;
}
if (!rawIsSafeString(str)) {
return false;
}
return arr.includes(str);
};
const rawAddStringToArray = (arr, str) => {
if (!rawIsSafeString(str)) {
return [];
}
if (!rawIsArray(arr)) {
return [str];
}
return arr.concat(str);
};
const rawTryAddStringToArray = (arr, str) => {
if (!rawIsSafeString(str)) {
return false;
}
if (!rawIsArray(arr)) {
return false;
}
if (rawFindStringInArray(arr, str)) {
return false;
}
try {
arr.push(str);
return true;
}
catch {
return false;
}
};
const rawRemoveStringFromArray = (arr, str) => {
if (!rawIsArray(arr) || !rawIsSafeString(str)) {
return false;
}
const idx = arr.indexOf(str);
if (-1 === idx) {
return false;
}
try {
arr.splice(idx, 1);
return true;
}
catch {
return false;
}
};
const rawCompareArray = (arr1, arr2, strict) => {
if (!rawIsArray(arr1) || !rawIsArray(arr2)) {
return false;
}
if (arr1.length !== arr2.length) {
return false;
}
if (rawIsBoolean(strict) && true === strict) {
return (JSON.stringify(arr1) === JSON.stringify(arr2));
}
else {
const keyCountMap = new Map();
for (const value1 of arr1) {
const key = JSON.stringify(value1);
keyCountMap.set(key, (keyCountMap.get(key) ?? 0) + 1);
}
for (const value2 of arr2) {
const key = JSON.stringify(value2);
const count = keyCountMap.get(key);
if (!count) {
return false;
}
if (1 === count) {
keyCountMap.delete(key);
}
else {
keyCountMap.set(key, count - 1);
}
}
return (0 === keyCountMap.size);
}
};
const rawMergeArray = (arr1, arr2) => {
if (!rawIsArray(arr1) || !rawIsArray(arr2)) {
return [];
}
if (!rawIsArray(arr1)) {
return arr2.slice();
}
if (!rawIsArray(arr2)) {
return arr1.slice();
}
const result = arr1.slice();
const seen = new Set(result);
for (const value of arr2) {
if (!seen.has(value)) {
seen.add(value);
result.push(value);
}
}
return result;
};
//
// Get difference from base to new array elements.
// If is_deleted_element is true, returns result difference elements for deleting from base.
// If is_deleted_element is false, returns result difference elements for adding from base.
//
const rawGetDiffArray = (basearr, newarr, is_deleted_element) => {
const barr = rawIsArray(basearr) ? basearr : [];
const narr = rawIsArray(newarr) ? newarr : [];
if (!rawIsNotEmptyArray(barr) && !rawIsNotEmptyArray(narr)) {
return [];
}
else if (!rawIsNotEmptyArray(barr) && rawIsNotEmptyArray(narr)) {
return is_deleted_element ? [] : narr.slice();
}
else if (rawIsNotEmptyArray(barr) && !rawIsNotEmptyArray(narr)) {
return is_deleted_element ? barr.slice() : [];
}
if (is_deleted_element) {
const seenNew = new Set(narr);
const result = [];
for (const value of barr) {
if (!seenNew.has(value)) {
result.push(value);
}
}
return result;
}
else {
const seenBase = new Set(barr);
const result = [];
for (const value of narr) {
if (!seenBase.has(value)) {
result.push(value);
}
}
return result;
}
};
const rawMergeObjects = (obj1, obj2) => {
const localSafeKey = (key) => {
return ('__proto__' !== key && 'constructor' !== key && 'prototype' !== key);
};
const _obj1 = rawIsPlainObject(obj1) ? obj1 : {};
const _obj2 = rawIsPlainObject(obj2) ? obj2 : {};
const resobj = {};
for (const key of Object.keys(_obj1)) {
if (localSafeKey(key)) {
resobj[key] = _obj1[key];
}
}
for (const key of Object.keys(_obj2)) {
if (localSafeKey(key)) {
resobj[key] = _obj2[key];
}
}
return resobj;
};
const rawMergeValTypeAllObject = (obj1, obj2) => {
const localSafeKey = (key) => {
return ('__proto__' !== key && 'constructor' !== key && 'prototype' !== key);
};
const _obj1 = rawIsValTypeAllObject(obj1) ? obj1 : {};
const _obj2 = rawIsValTypeAllObject(obj2) ? obj2 : {};
const resobj = {};
// copy entries from obj1
for (const key of Object.keys(_obj1)) {
if (localSafeKey(key)) {
const val = _obj1[key];
resobj[key] = rawIsValTypeAll(val) ? val : null;
}
}
// copy/overwrite entries from obj2
for (const key of Object.keys(_obj2)) {
if (localSafeKey(key)) {
const val = _obj2[key];
resobj[key] = rawIsValTypeAll(val) ? val : null;
}
}
return resobj;
};
//---------------------------------------------------------
// Utilities for time
//---------------------------------------------------------
//
// base is UTC
//
const rawConvertUnixtime = (base) => {
let unixtime_ms;
if (!rawIsSafeString(base)) {
unixtime_ms = Date.now();
}
else {
const trimBase = base.trim();
const tmp_ms = new Date(trimBase).getTime();
unixtime_ms = Number.isFinite(tmp_ms) ? tmp_ms : Date.now();
}
return Math.floor(unixtime_ms / 1000);
};
const rawCalcExpire = (expiredate) => {
let expire = rawConvertUnixtime(expiredate) - rawConvertUnixtime();
if (expire < 0) {
expire = 0;
}
return expire;
};
const rawIsExpired = (base) => {
return (rawConvertUnixtime(base) < rawConvertUnixtime()); // base(UTC ISO 8601) < now
};
const rawConvertISOStringToUnixtime = (iso) => {
if (!rawIsSafeString(iso)) {
return 0;
}
const isoBase = iso.trim();
const tmp_ms = new Date(isoBase).getTime();
const iso_ms = Number.isFinite(tmp_ms) ? tmp_ms : Date.now();
return Math.floor(iso_ms / 1000);
};
const rawGetExpireUnixtime = (starttime, expiretime) => {
return (expiretime - starttime);
};
const rawGetExpireUnixtimeFromISOStrings = (startiso, expireiso) => {
const starttime = rawConvertISOStringToUnixtime(startiso);
const expiretime = rawConvertISOStringToUnixtime(expireiso);
return rawGetExpireUnixtime(starttime, expiretime);
};
//---------------------------------------------------------
// Utilities for Key and Hierarchy
//---------------------------------------------------------
//
// Convert string to Hierarchy Array by separator
// ex) "parent-", "a/b/c" => ["parent-", "parent-a", "parent-a/b", "parent-a/b/c"]
// null, "a/b/c" => ["a", "a/b", "a/b/c"]
//
const rawExpandHierarchyArray = (parent, str, separator, allow_empty) => {
if (!rawIsSafeString(str)) {
return null;
}
const sep = rawIsSafeString(separator) ? separator : '/';
if (!rawIsSafeEntity(allow_empty)) {
allow_empty = false;
}
else if (!rawIsBoolean(allow_empty)) {
return null;
}
const result = [];
let tmp = '';
if (rawIsSafeString(parent)) {
result.push(parent);
tmp = parent;
}
const parts = str.split(sep);
for (let cnt = 0; cnt < parts.length; ++cnt) {
if (0 !== cnt) {
tmp += sep;
}
const tmpstr = rawGetSafeString(parts[cnt]);
tmp += tmpstr;
if (allow_empty || '' !== tmp) {
result.push(tmp);
}
}
return result;
};
//
// Convert parent and terminal children(string or array) to Hierarchy object
// The result is the object which is configured "key=parent", "value=parent's children".
//
// ex) parent = "parent-" ===> result = { "parent": ["parent-a"],
// children = "a/b/c" "parent-a": ["parent-a/b"],
// "parent-a/b": ["parent-a/b/c"],
// "parent-a/b/c": []
// };
//
// ex) parent = "parent-" ===> result = { "parent": ["parent-a", "parent-1"],
// children = [ "a/b/c", "parent-a": ["parent-a/b"],
// "1/2/3"] "parent-a/b": ["parent-a/b/c"],
// "parent-a/b/c": [],
// "parent-1": ["parent-1/2"],
// "parent-1/2": ["parent-1/2/3"],
// "parent-1/2/3": []
// };
//
// ex) parent = "" ===> result = { "a": ["a/b"],
// children = ["a/b/c"] "a/b": ["a/b/c"],
// "a/b/c": []
// };
//
// ex) parent = "" ===> result = null(because this case does not have hierarchy)
// children = "a"
//
const rawExpandHierarchy = (parent, children, separator, allow_empty) => {
if (!rawIsSafeString(parent)) {
parent = '';
}
let tmpchildren;
if (Array.isArray(children)) {
tmpchildren = children.slice();
}
else if (rawIsSafeString(children)) {
tmpchildren = [children];
}
else {
return null;
}
let is_set = false;
const result = {};
let parent_in_array;
for (let cnt = 0; cnt < tmpchildren.length; ++cnt) {
// parent + tmpchildren[x] ---> ["parent", "parent child1", "parent child1 sep child2", ...]
const child_hierarchy_arr = rawExpandHierarchyArray(parent, tmpchildren[cnt], separator, allow_empty);
if (null === child_hierarchy_arr || !Array.isArray(child_hierarchy_arr) || 0 === child_hierarchy_arr.length) {
continue;
}
parent_in_array = '';
for (let cnt2 = 0; cnt2 < child_hierarchy_arr.length; ++cnt2) {
if ('' !== parent_in_array) {
// parent ---> [child, child, ...]
if (!rawFindStringInArray(result[parent_in_array], child_hierarchy_arr[cnt2])) {
result[parent_in_array].push(child_hierarchy_arr[cnt2]);
is_set = true;
}
}
parent_in_array = child_hierarchy_arr[cnt2];
if (!rawIsSafeEntity(result[parent_in_array]) || !Array.isArray(result[parent_in_array])) {
result[parent_in_array] = new Array(0);
}
}
}
return (is_set ? result : null);
};
//
// Get direct parent path for key
// ex) parent: "parent", childkey: ":a/b/c" => parent:a/b
// parent: null, childkey: ":a/b/c" => :a/b
// parent: "parent", childkey: ":a" => parent
// parent: "parent", childkey: "" => null
//
const rawGetParentKey = (parent, childkey, separator, allow_empty) => {
if (!rawIsSafeString(childkey)) {
return null;
}
const child_hierarchy_arr = rawExpandHierarchyArray(parent, childkey, separator, allow_empty);
if (null === child_hierarchy_arr || !Array.isArray(child_hierarchy_arr) || 0 === child_hierarchy_arr.length) {
return null;
}
if (1 === child_hierarchy_arr.length) {
// there is no parent, the array is only childkey(or parent)
return null;
}
return child_hierarchy_arr[child_hierarchy_arr.length - 2];
};
//
// Get parent path from string
//
// ex) "a/b/c" => "a/b"
// "a" => null
// "a/b/c/" => "a/b" : if allow_empty is false
// "a/b/c/" => "a/b/c" : if allow_empty is true
// "a//b/c" => "a//b"
// "a/b/c//" => "a/b" : if allow_empty is false
// "a/b/c//" => "a/b/c/" : if allow_empty is true
// "a/" => null : if allow_empty is false
// "a/" => "a" : if allow_empty is true
// "/a/b" => "/a"
// "/a" => null
// "a//b" => "a" : if allow_empty is false
// "a//b" => "a/" : if allow_empty is true
// "a/b///" => "a" : if allow_empty is false
// "a/b///" => "a/b//" : if allow_empty is true
//
const rawGetParentPath = (str, separator, allow_empty) => {
if (!rawIsSafeString(str)) {
return '';
}
if (!rawIsSafeEntity(allow_empty)) {
allow_empty = false;
}
else if (!rawIsBoolean(allow_empty)) {
return '';
}
if (!rawIsSafeString(separator)) {
separator = '/';
}
// escape if allow empty
if (allow_empty) {
// escape '\' --> '\\'
str = str.replace(/\\/g, '\\\\');
// last word is '/' --> add '\'
if ('/' === str[str.length - 1]) {
str += '\\';
}
// if '//' --> '/\/'
let tmp = '';
for (let cnt = 0; cnt < str.length; ++cnt) {
tmp += str[cnt];
if ('/' === str[cnt] && (cnt + 1) < str.length && '/' === str[cnt + 1]) {
tmp += '\\';
}
}
str = tmp;
}
// parse by separator
const parts = str.split(separator);
// remove last elements until it is not empty
while (0 < parts.length && !rawIsSafeString(parts[parts.length - 1])) {
parts.pop();
}
// remove last element
if (0 < parts.length) {
parts.pop();
}
// remove last elements until it is not empty
while (0 < parts.length && !rawIsSafeString(parts[parts.length - 1])) {
parts.pop();
}
// join with separator
let parent = parts.join(separator);
// unescape if allow empty
if (allow_empty && rawIsSafeString(parent)) {
// '\' --> '' or '\\' --> '\'
let tmp = '';
for (let cnt = 0; cnt < parent.length; ++cnt) {
if ('\\' === parent[cnt]) {
++cnt;
if (cnt < parent.length && '\\' === parent[cnt]) {
tmp += parent[cnt]; // = '\'
}
}
else {
tmp += parent[cnt];
}
}
parent = tmp;
}
return parent;
};
//
// Compare string by regex or string patterns(array)
//
const rawCompareStringByFormats = (str, regex_ptn, pattern_array) => {
if (!rawIsSafeString(str)) {
return null;
}
const tmpstr = str.toLowerCase();
if (regex_ptn && rawIsSafeEntity(regex_ptn)) {
const matches = tmpstr.match(regex_ptn);
if (null !== matches && rawIsNotEmptyArray(matches) && 2 <= matches.length) {
return tmpstr;
}
}
let local_ptnarr = [];
if (rawIsSafeString(pattern_array)) {
local_ptnarr = [pattern_array];
}
else if (rawIsArray(pattern_array)) {
local_ptnarr = pattern_array;
}
for (let cnt = 0; cnt < local_ptnarr.length; ++cnt) {
if (rawCompareCaseString(tmpstr, local_ptnarr[cnt])) {
return tmpstr;
}
}
return null;
};
//---------------------------------------------------------
// Utilities for regex
//---------------------------------------------------------
//
// return object = {
// result: true/false
// parameter: object(array) or null or '' or undefined
// }
//
const rawGetNormalizeParameter = (parameter, regex_ptn, pattern) => {
const resobj = { result: false, parameter: undefined };
if (rawIsSafeString(parameter) && rawCheckSimpleJSON(parameter)) {
parameter = JSON.parse(parameter);
}
if (!rawIsSafeEntity(parameter)) {
resobj.result = true;
resobj.parameter = null; // = not update type
}
else if (rawIsArray(parameter)) {
if (!rawIsNotEmptyArray(parameter)) {
resobj.result = true;
resobj.parameter = ''; // = clean up type
}
else {
resobj.result = true;
resobj.parameter = [];
for (let cnt = 0; cnt < parameter.length; ++cnt) {
const resstr = rawCompareStringByFormats(parameter[cnt], regex_ptn, pattern);
if (null === resstr) {
resobj.result = false;
resobj.parameter = null;
break;
}
resobj.parameter.push(resstr);
}
}
}
else if ('' === parameter) {
resobj.result = true;
resobj.parameter = ''; // = clean up type
}
else if (rawIsSafeString(parameter)) {
const tmparr = parameter.split(',');
if (rawIsArray(tmparr) && rawIsNotEmptyArray(tmparr)) {
resobj.result = true;
resobj.parameter = [];
for (let cnt = 0; cnt < tmparr.length; ++cnt) {
const resstr = rawCompareStringByFormats(tmparr[cnt], regex_ptn, pattern);
if (null === resstr) {
resobj.result = false;
resobj.parameter = null;
break;
}
resobj.parameter.push(resstr);
}
}
else {
resobj.result = true;
resobj.parameter = ''; // = clean up type
}
}
else {
resobj.result = false;
}
return resobj;
};
//---------------------------------------------------------
// Utilities for UUID4
//---------------------------------------------------------
// RFC4122
// https://www.ietf.org/rfc/rfc4122.txt
//
// UUID4 = xxxxxxxx-xxxx-Nxxx-Mxxx-xxxxxxxxxxxx
// N: 0x4X
// M: 0x{8,9,A,B}X
//
const rawGetBinUuid4 = () => {
const binUuid4 = crypto.randomBytes(16);
binUuid4[6] = (binUuid4[6] & 0x0f) | 0x40; // UUID4 must be 0x4X for pos 6 in buffer
binUuid4[8] = (binUuid4[8] & 0x3f) | 0x80; // UUID4 must be 0x{8,9,A,B}X for pos 8 in buffer
return binUuid4;
};
// [NOTE]
// If binary data is given, this is only a conversion to UUID format,
// not a method that enforces UUID4 format.
//
const rawGetStrUuid4 = (binUuid4) => {
const buf = Buffer.isBuffer(binUuid4) ? binUuid4 : rawGetBinUuid4();
if (!Buffer.isBuffer(buf) || buf.length < 16) {
return '';
}
const hex = buf.toString('hex'); // 16 bytes to over 32 characters
if (hex.length < 32) {
return '';
}
return [hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20, 32)].join('-');
};
const rawIsSafeStrUuid4 = (strUuid4) => {
if (!rawIsSafeString(strUuid4)) {
return false;
}
// split '-' separator and check each part
const strArray = strUuid4.split('-');
if (5 != strArray.length ||
0 == strArray[0].length || 8 < strArray[0].length ||
0 == strArray[1].length || 4 < strArray[1].length ||
0 == strArray[2].length || 4 < strArray[2].length ||
0 == strArray[3].length || 4 < strArray[3].length ||
0 == strArray[4].length || 12 < strArray[4].length) {
return false;
}
return true;
};
const rawFillZeroString = (str, size) => {
if (!rawIsSafeString(str)) {
return null;
}
if (!Number.isInteger(size) || size < str.length) {
return null;
}
return str.length < size ? str.padStart(size, '0') : str;
};
const rawCvtStrToBinUuid4 = (strUuid4) => {
if (!rawIsSafeString(strUuid4)) {
return null;
}
// split '-' separator
const strArray = strUuid4.split('-');
if (5 !== strArray.length) {
return null;
}
// check length and fill '0' for short part
strArray[0] = rawFillZeroString(strArray[0], 8) ?? '';
strArray[1] = rawFillZeroString(strArray[1], 4) ?? '';
strArray[2] = rawFillZeroString(strArray[2], 4) ?? '';
strArray[3] = rawFillZeroString(strArray[3], 4) ?? '';
strArray[4] = rawFillZeroString(strArray[4], 12) ?? '';
if ('' === strArray[0] || '' === strArray[1] || '' === strArray[2] || '' === strArray[3] || '' === strArray[4]) {
return null;
}
// uuid full hex string
const fullString = strArray.join('');
if (!/^[0-9a-fA-F]{32}$/.test(fullString)) {
return null;
}
// convert to binary array
try {
return Buffer.from(fullString, 'hex');
}
catch {
return null;
}
};
//
// Converts a decimal or hexadecimal character string into a Buffer array(hexadecimal)
// with a specified number of bytes.
//
// strNumber: string for decimal or hexadecimal number
// base: radix(10 or 16)
// size: output Buffer array size
//
// result: Buffer array
//
const rawCvtNumberStringToBinBuffer = (strNumber, base, size) => {
if (!rawIsSafeString(strNumber)) {
return null;
}
if (16 != base && 10 != base) {
return null;
}
if (10 == base) {
// convert dec string to hex string
const tmpNumber = parseInt(strNumber, 10);
strNumber = tmpNumber.toString(16);
}
const matched = strNumber.match(/.{1,2}/g);
const uintarr = new Uint8Array((matched ?? []).map((val) => parseInt(val, 16)));
const binbuff = Buffer.alloc(size);
binbuff.fill(0);
if (size < uintarr.length) {
// If input number array length is larger than required length, fill bottom.
const diff_length = uintarr.length - size;
for (let cnt = uintarr.length; diff_length < cnt; --cnt) {
binbuff[cnt - 1 - diff_length] = uintarr[cnt - 1];
}
}
else {
// If required length is larger than input length, fill zero to top.
const diff_length = size - uintarr.length;
for (let cnt = size; 0 < cnt; --cnt) {
if ((cnt - 1) < diff_length) {
break;
}
binbuff[cnt - 1] = uintarr[cnt - 1 - diff_length];
}
}
return binbuff;
};
//
// Return result
// {
// hi_id: Buffer[16] for hi uuid
// low_id: Buffer[16] for low uuid
// base: Buffer[32] for base
// str_token: hex[32] string for token
// token: Buffer[32] for token
// }
//
const rawMakeToken256 = (hiUuid4, lowUuid4, base) => {
// hiUuid4[128] / lowUuid4[128]
if (!rawIsSafeEntity(hiUuid4) || !(Buffer.isBuffer(hiUuid4)) || 16 != hiUuid4.length ||
!rawIsSafeEntity(lowUuid4) || !(Buffer.isBuffer(lowUuid4)) || 16 != lowUuid4.length ||
!rawIsSafeEntity(base) || !(Buffer.isBuffer(base)) || 32 != base.length) {
return null;
}
const result = {
hi_id: hiUuid4,
low_id: lowUuid4,
base: base,
str_token: '',
token: Buffer.alloc(0)
};
// raw not crypted token[256]
const token = Buffer.alloc(32);
for (let cnt = 0; cnt < 32; ++cnt) {
if (cnt < 16) {
token[cnt] = result.base[cnt] ^ hiUuid4[cnt];
}
else {
token[cnt] = result.base[cnt] ^ lowUuid4[cnt - 16];
}
}
// sha256 token[256]
const cryptSha256 = crypto.createHash('sha256');
cryptSha256.update(token);
result.str_token = cryptSha256.digest('hex');
// sha256 binary token[256]
result.token = rawCvtNumberStringToBinBuffer(result.str_token, 16, 32) ?? Buffer.alloc(0);
return result;
};
//
// Return result
// {
// str_hi_id: hi uuid string
// hi_id: Buffer[16] for hi uuid
// str_low_id: low uuid string
// low_id: Buffer[16] for low uuid
// str_base: base string
// base: Buffer[32] for base
// str_token: hex[32] string for token
// token: Buffer[32] for token
// }
//
const rawMakeStringToken256 = (strHiUuid4, strLowUuid4, strBase) => {
if (!rawIsSafeString(strHiUuid4) || !rawIsSafeString(strLowUuid4)) {
return null;
}
const hiUuid4 = rawCvtStrToBinUuid4(strHiUuid4);
const lowUuid4 = rawCvtStrToBinUuid4(strLowUuid4);
if (null === hiUuid4 || null === lowUuid4) {
return null;
}
let base;
if (rawIsString(strBase)) {
// string base is specified as string, then convert to buffer[32]
base = rawCvtNumberStringToBinBuffer(strBase, 16, 32);
}
else if (rawIsSafeNumber(strBase)) {
// string base is specified as number, then convert to buffer[32]
base = rawCvtNumberStringToBinBuffer(String(strBase), 16, 32);
}
else { // null === strBase
// base is not specified, then it is made at here.
base = crypto.randomBytes(32); // base data is random value in buffer[32]
}
if (null === base) {
return null;
}
const binResult = rawMakeToken256(hiUuid4, lowUuid4, base);
if (null === binResult) {
return null;
}
const result = {
str_hi_id: strHiUuid4,
hi_id: binResult.hi_id,
str_low_id: strLowUuid4,
low_id: binResult.low_id,
str_base: base.toString('hex'),
base: binResult.base,
str_token: binResult.str_token,
token: binResult.token,
};
return result;
};
const rawCvtNumberStringToUuid4 = (strNumber, base) => {
const bin_uuid4 = rawCvtNumberStringToBinBuffer(strNumber, base, 16); // UUID4 is 16bytes
if (null == bin_uuid4) {
return null;
}
return rawGetStrUuid4(bin_uuid4);
};
//---------------------------------------------------------
// Utilities for Host/IP address
//---------------------------------------------------------
//
// Get client ip address
//
// [NOTE]
// If the server is behind of proxy, you have to set "trust proxy" to express app.
// Then we get client ip address in X-Forwarded-for header from req.ip.
//
// If you do not use "trust proxy", you can get client ip by following:
//
// ip = (rawIsSafeEntity(req.headers) && rawIsSafeEntity(req.headers['x-forwarded-for'])) ? req.headers['x-forwarded-for'].split(',')[0] :
// (rawIsSafeEntity(req.connection) && rawIsSafeString(req.connection.remoteAddress)) ? req.connection.remoteAddress :
// (rawIsSafeEntity(req.connection) && rawIsSafeEntity(req.connection.socket) && rawIsSafeString(req.connection.socket.remoteAddress)) ? req.connection.socket.remoteAddress :
// (rawIsSafeEntity(req.socket) && rawIsSafeString(req.socket.remoteAddress)) ? req.socket.remoteAddress :
// '0.0.0.0';
// ip = ip.split(':').slice(-1); //in case the ip returned in a format: "::ffff:xxx.xxx.xxx.xxx"
//
const rawGetClientIpAddress = (req) => {
// If "trust proxy" is set, set client ip address to req.ip.
let ip = (rawIsSafeEntity(req) && rawIsSafeString(req.ip)) ? (req.ip ?? null) : null;
if (!rawIsSafeString(ip)) {
ip = null;
}
else {
// check format "::ffff:" for ipv4 on ipv6
if ('::ffff:' === ip.substr(0, 7)) {
ip = ip.substr(7);
}
}
return ip;
};
const rawCompareRequestIpAddress = (req, ip) => {
const req_ip = rawGetClientIpAddress(req);
if (null === req_ip) {
return false;
}
return (req_ip === ip);
};
//
// Get hostname from ip address
//
// ip : ip address
// callback : function(err message, result<string[]>)
//
const rawGetHostnameFromIpAddress = (ip, callback) => {
if (!rawIsSafeString(ip)) {
callback('ip address parameter is wrong', null);
return;
}
dns.reverse(ip, (err, hosts) => {
if (rawIsSafeEntity(err)) {
callback('ip address(' + ip + ') is not convert hostname: ' + err.message, null);
return;
}
if (!rawIsNotEmptyArray(hosts)) {
callback('ip address(' + ip + ') is not convert hostname', null);
return;
}
callback(null, hosts);
});
};
//
// Get ip address from hostname
//
// hostname : host name
// callback : function(err message, result<string[]>)
//
const rawGetIpAddressFromHostname = (hostname, callback) => {
if (!rawIsSafeString(hostname)) {
callback('hostname parameter is wrong', null);
return;
}
// try ipv4
const _hostname4 = hostname;
dns.resolve4(_hostname4, (err, ip4) => {
if (rawIsSafeEntity(err) || !rawIsNotEmptyArray(ip4)) {
// try ipv6
const _hostname6 = _hostname4;
dns.resolve6(_hostname6, (err, ip6) => {
if (rawIsSafeEntity(err) || !rawIsNotEmptyArray(ip6)) {
let strErrMsg = 'hostname(' + _hostname6 + ') is not convert ipv6 address : ';
if (rawIsSafeEntity(err) && rawIsSafeString(err.message)) {
strErrMsg += err.message;
}
callback(strErrMsg, null);
return;
}
callback(null, ip6);
});
}
callback(null, ip4);
});
};
//
// Complement ip address and hostname
//
// hostname : host name
// ip : ip address
// callback : function(err message, ip array[ip,...], hostname array[name,...])
//
const rawComplementHostnameIpAddress = (hostname, ip, callback) => {
if (!rawIsSafeString(hostname) && !rawIsSafeString(ip)) {
callback('hostname and ip parameter is wrong', null, null);
return;
}
if (rawIsSafeString(hostname) && rawIsSafeString(ip)) {
const ips = [ip];
const hosts = [hostname];
callback('hostname and ip parameter is wrong', ips, hosts);
return;
}
if (rawIsSafeString(hostname)) {
const _hostname = [hostname];
rawGetIpAddressFromHostname(ip, (err, result) => {
if (rawIsSafeEntity(err)) {
callback(err, null, null);
return;
}
callback(null, result, _hostname);
});
}
else if (rawIsSafeString(ip)) {
const _ips = [ip];
rawGetHostnameFromIpAddress(ip, (err, result) => {
if (rawIsSafeEntity(err)) {
callback(err, null, null);
return;
}
callback(null, _ips, result);
});
}
};
//
// url parser with default port
//
const rawUrlParse = (strurl) => {
const ep = url.parse(strurl);
if (rawIsSafeEntity(ep) && (null === ep.port || !rawIsSafeEntity(ep.port) || isNaN(Number(ep.port)))) {
// set default port
if (rawIsSafeString(ep.protocol) && 'https:' === ep.protocol) {
ep.port = String(443);
}
else {
ep.port = String(80);
}
}
return ep;
};
//
// Check hostname/ip address
//
// host : hostname or ip address string
//
const simple_reg_ipv4 = /^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
const simple_reg_ipv6 = /^(([0-9]|[a-f]|[A-F])*:)*([0-9]|[a-f]|[A-F])$/;
const rawIsIpAddressString = (host) => {
if (!rawIsSafeString(host)) {
return false;
}
if (host.match(simple_reg_ipv4) || host.match(simple_reg_ipv6)) {
return true;
}
return false;
};
//
// Check URL
//
// Allow formatted as "http(s)://<host{:port}>{/{<path>}...}"
//
// Result matches by regex
// matches.length 7
// matches[0] input string(url)
// matches[1] method string(http or https)
// matches[2] FQDN string(domain name)
// matches[3] port number starting with ':'(':xxxx'). if url does not have port, this value is ''(empty string)
// matches[4] port number starting with ':'(':xxxx'). if url does not have port, this value is undefined
// matches[5] url path and arguments string(string after port number). if url end of character is port number, this value is ''(empty string)
// matches[6] url path and arguments string(string after port number). if url end of character is port number, this value is undefined
//
// Ex.)
// Input url : 'https://abc.co.jp:8080/path?arg=value'
// matches : [ 'https://abc.co.jp:8080/path?arg=value',
// 'https',
// 'abc.co.jp',
// ':8080',
// ':8080',
// '/path?arg=value',
// '/path?arg=value',
// index: 0,
// input: 'https://abc.co.jp:8080/path?arg=value'
// ]
//
// Input url : 'https://abc.co.jp/'
// matches : [ 'https://abc.co.jp/',
// 'https',
// 'abc.co.jp',
// '',
// undefined,
// '',
// undefined,
// index: 0,
// input: 'https://abc.co.jp/'
// ]
//
const reg_url = /^(https?):\/\/([a-z|A-Z|0-9|$|%|&|(|)|-|=|~|^|||@|+|.|_]+)((:[1-9]\d*)?)((\/.*)*)$/;
const rawIsSafeUrl = (strurl) => {
if (!rawIsSafeString(strurl)) {
return false;
}
if (null === strurl.match(reg_url)) {
return false;
}
return true;
};
const rawParseUrl = (strurl) => {
if (!rawIsSafeString(strurl)) {
return null;
}
const matches = strurl.match(reg_url);
if (null === matches || !rawIsNotEmptyArray(matches) || matches.length < 7) {
return null;
}
const resobj = { https: false, host: '', path: '', port: 0 };
resobj.https = rawCompareCaseString(matches[1], 'https');
resobj.host = matches[2];
resobj.path = matches[5];
if (rawIsSafeString(matches[3]) && !isNaN(Number(matches[3].substr(1)))) {
resobj.port = parseInt(matches[3].substr(1));
}
else {
resobj.port = resobj.https ? 443 : 80;
}
return resobj;
};
const rawCheckFileExist = (file) => {
if (!rawIsSafeString(file)) {
return false;
}
try {
fs.statSync(file);
}
catch {
return false;
}
return true;
};
const rawReadFileContents = (file) => {
if (!rawIsSafeString(file)) {
return null;
}
if (!rawCheckFileExist(file)) {
return null;
}
try {
return fs.readFileSync(file).toString();
}
catch {
return null;
}
};
const rawCheckDir = (path) => {
if (!rawIsSafeString(path)) {
return false;
}
return fs.existsSync(path);
};
const rawCheckMakeDir = (path) => {
if (!rawIsSafeString(path)) {
return false;
}
if (rawCheckDir(path)) {
return true;
}
try {
fs.mkdirSync(path);
}
catch {
return false;
}
return true;
};
//---------------------------------------------------------
// Utilities for dnamic import
//---------------------------------------------------------
const rawTryLoadModule = async (filepath) => {
const candidates = filepath.endsWith('.js') ? [filepath] : [filepath, (filepath + '.js')];
const errors = [];
for (const cand of candidates) {
try {
const modRaw = (await import(cand));
if (rawIsPlainObject(modRaw)) {
const defaultExport = modRaw.default || modRaw;
if (rawIsPlainObject(defaultExport)) {
const keys = Object.keys(defaultExport);
if (0 < keys.length) {
const firstKey = keys[0];
return defaultExport[firstKey];
}
else {
return defaultExport;
}
}
else {
return defaultExport;
}
}
else {
return modRaw;
}
}
catch (error) {
errors.push(cand + ' -> ' + JSON.stringify(error));
}
}
console.debug('import failed : ', JSON.stringify(errors));
return null;
};
//---------------------------------------------------------
// Utilities for variables defined in type.ts
//---------------------------------------------------------
const rawIsValTypeTokenSeed = (val) => {
if (!rawIsPlainObject(val)) {
return false;
}
const _obj = val;
const _isString = (key) => rawIsString(_obj[key]);
const _isStringOrNull = (key) => null === _obj[key] || rawIsString(_obj[key]);
const _isFiniteNumber = (key) => rawIsSafeNumber(_obj[key]);
return (_isString('publisher') &&
_isString('userexid') &&
_isString('date') &&
_isString('expire') &&
_isString('creator') &&
_isString('base') &&
_isStringOrNull('user') &&
_isStringOrNull('ip') &&
_isStringOrNull('hostname') &&
_isFiniteNumber('port') &&
_isStringOrNull('cuk') &&
_isStringOrNull('extra') &&
_isStringOrNull('tenant'));
};
const rawIsValTypeRoleInfo = (val) => {
if (!rawIsPlainObject(val)) {
return false;
}
if ((undefined !== val.role && !rawIsString(val.role)) ||
(undefined !== val.token && !rawIsString(val.token))) {
return false;
}
for (const [, value] of Object.entries(val)) {
if (!rawIsValTypeAll(value)) {
return false;
}
}
return true;
};
//---------------------------------------------------------
// Exports
//---------------------------------------------------------
exports.k2hr3ppiutil = {
isSafeEntity: rawIsSafeEntity,
isString: rawIsString,
isSafeString: rawIsSafeString,
isPlainObject: rawIsPlainObject,
isValTypeAllObject: rawIsValTypeAllObject,
isValTypeAll: rawIsValTypeAll,
isFunction: rawIsFunction,
isBoolean: rawIsBoolean,
isSafeNumber: rawIsSafeNumber,
isSafeNumeric: rawIsSafeNumeric,
cvtToNumber: rawCvtToNumber,
compareCaseString: rawCompareCaseString,
isArray: rawIsArray,
isStringArray: rawIsStringArray,
isString2DArray: rawIsString2DArray,
hasPartString: rawHasPartString,
getSafeString: rawGetSafeString,
checkSimpleJSON: rawCheckSimpleJSON,
parseJSON: rawParseJSON,
isNotEmptyArray: rawIsNotEmptyArray,
getSafeArray: rawGetSafeArray,
getSafeStringArray: rawGetSafeStringArray,
findStringInArray: rawFindStringInArray,
addStringToArray: rawAddStringToArray,
tryAddStringToArray: rawTryAddStringToArray,
removeStringFromArray: rawRemoveStringFromArray,
compareArray: rawCompareArray,
mergeArray: rawMergeArray,
getDeletingDifferenceArray: (basearr, newarr) => rawGetDiffArray(basearr, newarr, true),
getAddingDifferenceArray: (basearr, newarr) => rawGetDiffArray(basearr, newarr, false),
mergeObjects: rawMergeObjects,
mergeValTypeAllObject: rawMergeValTypeAllObject,
getUnixtime: rawConvertUnixtime,
calcExpire: rawCalcExpire,
isExpired: rawIsExpired,
convertISOStringToUnixtime: rawConvertISOStringToUnixtime,
getExpireUnixtime: rawGetExpireUnixtime,
getExpireUnixtimeFromISOStrings: rawGetExpireUnixtimeFromISOStrings,
expandHierarchy: rawExpandHierarchy,
getParentKey: rawGetParentKey,
getParentPath: rawGetParentPath,
getNormalizeParameter: rawGetNormalizeParameter,
getBinUuid4: rawGetBinUuid4,
getStrUuid4: rawGetStrUuid4,
isSafeStrUuid4: rawIsSafeStrUuid4,
cvtStrToBinUuid4: rawCvtStrToBinUuid4,
cvtNumberStringToBinBuffer: rawCvtNumberStringToBinBuffer,
makeToken256: rawMakeToken256,
makeStringToken256: rawMakeStringToken256,
cvtNumberStringToUuid4: rawCvtNumberStringToUuid4,
getClientIpAddress: rawGetClientIpAddress,
compareRequestIpAddress: rawCompareRequestIpAddress,
complementHostnameIpAddress: rawComplementHostnameIpAddress,
urlParse: rawUrlParse,
isIpAddressString: rawIsIpAddressString,
isSafeUrl: rawIsSafeUrl,
parseUrl: rawParseUrl,
checkFileExist: rawCheckFileExist,
readFileContents: rawReadFileContents,
checkDir: rawCheckDir,
checkMakeDir: rawCheckMakeDir,
tryLoadModule: rawTryLoadModule,
isValTypeTokenSeed: rawIsValTypeTokenSeed,
isValTypeRoleInfo: rawIsValTypeRoleInfo
};
exports.default = exports.k2hr3ppiutil;
/*
* 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
*/