k2hr3-app
Version:
K2HR3 Web Application is K2hdkc based Resource and Roles and policy Rules
1,761 lines (1,586 loc) • 76.9 kB
JavaScript
/*
*
* K2HR3 Web Application
*
* 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: Tue Sep 5 2017
* REVISION:
*
*/
import R3Context from '../util/r3context';
import { r3GetTextRes } from '../util/r3define';
import { resourceType, roleType, policyType, serviceType } from '../util/r3types';
import { checkServiceResourceValue } from '../util/r3verifyutil';
import { r3DeepClone, r3ObjMerge, parseCombineHostObject, r3IsEmptyEntity, r3IsEmptyEntityObject, r3IsEmptyStringObject, r3IsSafeTypedEntity, r3IsEmptyString, r3CompareCaseString } from '../util/r3util';
//
// K2HR3 Data Provider Class
//
export default class R3Provider
{
constructor(cbProgressControl, signin, username, unscopedtoken)
{
this.tokenHeaderType = {
noUserToken: -1,
unscopedUserToken: -2
};
this.r3Context = new R3Context(signin, username, unscopedtoken);
// caches
//
// [TODO]
// Need to implement timeout
//
this.scopedUserToken = {}; // Scoped User Tokens - Key is Tenant name
this.tenantList = []; // Tenant name list(element is object = {name: "tenant name", display: "display name"})
this.cbProgressControl = cbProgressControl; // Callback function for progress(allow null)
// text resource
this.r3TextRes = r3GetTextRes(this.r3Context.getSafeLang());
}
getR3Context()
{
return this.r3Context;
}
getR3TextRes()
{
return this.r3TextRes;
}
startProgress()
{
if(null !== this.cbProgressControl){
this.cbProgressControl(true);
}
}
stopProgress()
{
if(null !== this.cbProgressControl){
this.cbProgressControl(false);
}
}
//--------------------------------------------------
// FETCH
//--------------------------------------------------
// raw methods
//
_fetch(path, method, headers, tokenType, body, isCvtBodyJSON, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _path = r3IsEmptyString(path) ? '/' : encodeURI(path).replace(/#/g, '%23');// force replace '#'
let _url = this.r3Context.getApiUrlBase() + _path;
let _method = r3IsEmptyString(method) ? 'GET' : method; // default is GET
let _length = 0;
let _strBody;
if(!r3IsEmptyEntity(body)){
if(!r3IsSafeTypedEntity(isCvtBodyJSON, 'boolean') || !isCvtBodyJSON){
_strBody = body;
}else{
_strBody = JSON.stringify(body);
}
_length = _strBody.length;
}
let _headers = r3IsEmptyEntity(headers) ? {} : headers;
_headers = r3ObjMerge(_headers, {
'Content-Type': 'application/json',
'Content-Length': _length,
});
this.r3Context.getDbgHeader(_headers); // Add debug header if development environment
// get token
this.getUserTokenByType(tokenType, (error, token) =>
{
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
if(r3IsSafeTypedEntity(token, 'string')){
_headers['x-auth-token'] = 'U=' + token;
}
let _fetchOpt = {
mode: 'cors',
method: _method,
headers: _headers
};
if(undefined !== _strBody && null !== _strBody){
_fetchOpt.body = _strBody;
}
this.startProgress(); // start progressing
// Send request
fetch(_url, _fetchOpt).then(response => {
if(!response.ok){
let dbgresheader= this.r3Context.getSafeDbgResHeaderName();
let errorinfo = '';
if('' !== dbgresheader && response.headers.has(dbgresheader)){
errorinfo = response.headers.get(dbgresheader);
if(null === errorinfo){
errorinfo = '';
}
}
let _errobj = new Error('REQUEST = ' + decodeURI(_path) + ', STATUS = ' + response.statusText + '(' + String(response.status) + '), ERROR HEADER = ' + errorinfo);
_errobj.status = response.status;
// [NOTE]
// Do not call stopProgress() here, it is called after throwing exception.
//
throw _errobj;
}
if(204 === response.status){
// 204 does not have response body, thus make resobj here.
return { result: true, message: null };
}
return response.json();
}).then(resobj => {
this.stopProgress(); // stop progressing
if(r3IsEmptyEntity(resobj) || (r3IsSafeTypedEntity(resobj.result, 'boolean') && true !== resobj.result)){
throw new Error('REQUEST = ' + decodeURI(_path) + ', ERROR = Response is sonmething wrong or false: ' + JSON.stringify(resobj));
}
_callback(null, resobj);
return;
}).catch(error => {
this.stopProgress(); // stop progressing
if(r3IsEmptyEntity(error)){
error = new Error('K2HR3 API ERROR => Unknown reason.');
error.status = 500;
}else{
error.message = 'K2HR3 API ERROR => ' + error.message;
if(undefined === error.status || null === error.status){
error.status= 500;
}
}
console.error(error.message);
_callback(error, null);
return;
});
});
}
//
// GET raw method
//
_get(path, urlargs, headers, tokenType, callback)
{
if(!r3IsEmptyString(path) && !r3IsEmptyString(urlargs)){
path += '?' + urlargs;
}
return this._fetch(path, 'GET', headers, tokenType, null, false, callback);
}
//
// HEAD raw method
//
_head(path, urlargs, headers, tokenType, callback)
{
if(!r3IsEmptyString(path) && !r3IsEmptyString(urlargs)){
path += '?' + urlargs;
}
return this._fetch(path, 'HEAD', headers, tokenType, null, false, callback);
}
//
// PUT raw method
//
_put(path, urlargs, headers, tokenType, callback)
{
if(!r3IsEmptyString(path) && !r3IsEmptyString(urlargs)){
path += '?' + urlargs;
}
return this._fetch(path, 'PUT', headers, tokenType, null, false, callback);
}
//
// POST raw method
//
_post(path, headers, tokenType, body, isCvtBodyJSON, callback)
{
return this._fetch(path, 'POST', headers, tokenType, body, isCvtBodyJSON, callback);
}
//
// DELETE raw method
//
_delete(path, urlargs, headers, tokenType, callback)
{
if(!r3IsEmptyString(path) && !r3IsEmptyString(urlargs)){
path += '?' + urlargs;
}
return this._fetch(path, 'DELETE', headers, tokenType, null, false, callback);
}
//--------------------------------------------------
// TOKEN
//--------------------------------------------------
//
// raw get Unscoped User token
//
getUnscopedUserToken(username, passphrase, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let error;
if(r3IsEmptyString(username, true)){
error = new Error('username parameter is empty');
console.error(error.message);
callback(error, null);
return;
}
let _callback = callback;
let _username = username;
let _passphrase = r3IsEmptyString(passphrase, true) ? null : passphrase.trim(); // allow empty passphrase
let _body = {
'auth': {
'tenantName': '',
'passwordCredentials': {
'username': _username,
'password': _passphrase
}
}
};
this.startProgress(); // start progressing
this._post('/v1/user/tokens', null, this.tokenHeaderType.noUserToken, _body, true, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
if(!resobj.result){
let error = new Error('No result object.');
console.error(error.message);
_callback(error, null);
return;
}
if(true !== resobj.result || false !== resobj.scoped){
error = new Error('Response data is sonmething wrong: ' + JSON.stringify(resobj));
console.error(error.message);
_callback(error, null);
return;
}
_callback(null, resobj.token);
});
}
//
// raw get Scoped User token
//
getScopedUserToken(tenantname, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let error;
if(r3IsEmptyString(tenantname)){
error = new Error('tenantname parameter is wrong.');
console.error(error.message);
callback(error, null);
return;
}
if(!r3IsEmptyStringObject(this.scopedUserToken, tenantname)){
callback(null, this.scopedUserToken[tenantname]);
return;
}
let _callback = callback;
let _tenantname = tenantname;
let _body = {
'auth': {
'tenantName': _tenantname
}
};
this.startProgress(); // start progressing
this._post('/v1/user/tokens', null, this.tokenHeaderType.unscopedUserToken, _body, true, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
if(!resobj.result){
let error = new Error('No result object.');
console.error(error.message);
_callback(error, null);
return;
}
if(true !== resobj.result || true !== resobj.scoped){
error = new Error('Response data is sonmething wrong: ' + JSON.stringify(resobj));
console.error(error.message);
_callback(error, null);
return;
}
this.scopedUserToken[_tenantname] = resobj.token;
_callback(null, resobj.token);
});
}
//
// raw get Token( No token / Scoped / Unscoped ) method
//
// tokenType is number or string as tenant name.
// If it is number, it means no token(tokenHeaderType.noUserToken) or
// unscoped token(tokenHeaderType.unscopedUserToken).
// If string, it means tenant name. then this method sets scoped token
// to headers object.
//
// [NOTE]
// If there is no scoped token, this method calls API for getting scoped
// token for tenant.
//
getUserTokenByType(tokenType, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let error;
if(r3IsEmptyEntity(tokenType)){
error = new Error('tokenType parameter is wrong.');
console.error(error.message);
callback(error, null);
}else{
if(!isNaN(tokenType) && this.tokenHeaderType.noUserToken === tokenType){
// no token
callback(null, null);
}else if(!isNaN(tokenType) && this.tokenHeaderType.unscopedUserToken === tokenType){
// unscoped token
callback(null, this.r3Context.getSafeUnscopedToken());
}else if(!isNaN(tokenType) || !r3IsEmptyString(tokenType)){
if(!isNaN(tokenType)){
// force string
tokenType = String(tokenType);
}
let _callback = callback;
this.startProgress(); // start progressing
// scoped token
this.getScopedUserToken(tokenType, (error, token) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
_callback(null, token);
});
}else{
error = new Error('tokenType is not number nor string.');
console.error(error.message);
callback(error, null);
}
}
}
//--------------------------------------------------
// Tenant List
//--------------------------------------------------
//
// Get tenant list
//
getTenantList(force, useLocalTenant, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
if(force){
this.tenantList = [];
}
if(0 < this.tenantList.length){
// using cache
_callback(null, this.tenantList);
return;
}
if(!this.r3Context.isLogin()){
console.info('Not logged in yet.');
// return empty cache
_callback(null, this.tenantList);
return;
}
this.startProgress(); // start progressing
this._get('/v1/user/tokens', null, null, this.tokenHeaderType.unscopedUserToken, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
if(undefined !== error.status && 404 == error.status){
console.error('Could not get tenat list with response status is 404, thus return empty tenant list : ' + error.message);
this.tenantList = [];
_callback(null, this.tenantList);
}else{
console.error(error.message);
_callback(error, null);
}
return;
}
if(true !== resobj.result || false !== resobj.scoped || this.r3Context.getSafeUserName() !== resobj.user){
error = new Error('Response data is sonmething wrong: ' + JSON.stringify(resobj));
console.error(error.message);
_callback(error, null);
return;
}
if(!r3IsSafeTypedEntity(resobj.tenants, 'array')){
this.tenantList = [];
}else{
this.tenantList = resobj.tenants;
this.tenantList.sort( (tenant1, tenant2) =>
{
if(!r3IsEmptyString(tenant1.display) && !r3IsEmptyString(tenant2.display)){
return 0;
}
if(tenant1.display < tenant2.display){
return -1;
}else if(tenant1.display > tenant2.display){
return 1;
}
return 0;
});
}
if(!useLocalTenant){
//
// local tenant is invalid, so stop here
//
_callback(null, this.tenantList);
return;
}
//
// Get All local Tenant Infomration
//
this.startProgress(); // start progressing
this._get('/v1/tenant', 'expand=true', null, this.tokenHeaderType.unscopedUserToken, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
if(undefined !== error.status && 404 == error.status){
console.error('Could not get tenat list with response status is 404, thus return existed(normal) tenant list : ' + error.message);
_callback(null, this.tenantList);
}else{
console.error(error.message);
_callback(error, null);
}
return;
}
if(true !== resobj.result){
error = new Error('Could not get local tenant list : ' + resobj.message);
console.error(error.message);
_callback(error, null);
return;
}
//
// Add users data to local tenant information
//
if(r3IsSafeTypedEntity(resobj.tenants, 'array')){
for(let cnt = 0; cnt < resobj.tenants.length; ++cnt){
if(r3IsEmptyString(resobj.tenants[cnt].name)){
console.warn('The local tenant name in respose is empty, so skip this.');
continue;
}
let foundTenant = false;
for(let cnt2 = 0; cnt2 < this.tenantList.length; ++cnt2){
if(!r3IsEmptyString(this.tenantList[cnt2].name) && this.tenantList[cnt2].name == resobj.tenants[cnt].name){
//
// Add users
//
this.tenantList[cnt2].users = r3DeepClone(resobj.tenants[cnt].users);
foundTenant = true;
break;
}
}
if(!foundTenant){
console.warn('Not found ' + resobj.tenants[cnt].name + ' local tenant in current tenant list, so skip this.');
}
}
}else{
console.warn('Respose for getting local tenant list is something wrong, but continue...');
}
_callback(null, this.tenantList);
});
});
}
//--------------------------------------------------
// Local Tenant
//--------------------------------------------------
//
// Create Local Tenant
//
// name : local tenant name
// display : display name for new local tenant(allowed null/emptyv1/tenant)
// description : description for new local tenant(allowed null/emptyv1/tenant)
// users : initial user name array for new local tenant
//
createLocalTenant(name, display, description, users, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyString(name, true) || !r3IsSafeTypedEntity(users, 'array') || 0 === users.length){
_error = new Error('name(' + JSON.stringify(name) + ') or display(' + JSON.stringify(display) + ') or description(' + JSON.stringify(description) + ') or users(' + JSON.stringify(users) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _name = name.trim();
let _display = (r3IsEmptyString(display, true) ? null : r3IsEmptyString(display.trim(), true) ? null : display.trim());
let _description= (r3IsEmptyString(description, true) ? null : r3IsEmptyString(description.trim(), true) ? null : description.trim());
let _users = r3DeepClone(users);
let _body = {
'tenant': {
'name': _name,
'display': _display,
'desc': _description,
'users': _users
}
};
this.startProgress(); // start progressing
this._post('/v1/tenant', null, this.tokenHeaderType.unscopedUserToken, _body, true, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//
// Update Local Tenant
//
// name : local tenant name
// id : local tenant id
// display : display name for local tenant(allowed null/emptyv1/tenant)
// description : description for local tenant(allowed null/emptyv1/tenant)
// users : user name array for local tenant
//
updateLocalTenant(name, id, display, description, users, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyString(name, true) || r3IsEmptyString(id, true) || !r3IsSafeTypedEntity(users, 'array') || 0 === users.length){
_error = new Error('name(' + JSON.stringify(name) + ') or id(' + JSON.stringify(id) + ') or display(' + JSON.stringify(display) + ') or description(' + JSON.stringify(description) + ') or users(' + JSON.stringify(users) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _name = name.trim();
let _id = id.trim();
let _display = (r3IsEmptyString(display, true) ? null : r3IsEmptyString(display.trim(), true) ? null : display.trim());
let _description= (r3IsEmptyString(description, true) ? null : r3IsEmptyString(description.trim(), true) ? null : description.trim());
let _users = r3DeepClone(users);
let _url = '/v1/tenant/' + _name;
let _body = {
'tenant': {
'id': _id,
'display': _display,
'desc': _description,
'users': _users
}
};
this.startProgress(); // start progressing
this._post(_url, null, this.tokenHeaderType.unscopedUserToken, _body, true, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//
// Delete Local Tenant
//
// name : local tenant name
// id : local tenant id
//
deleteLocalTenant(name, id, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyString(name, true) || r3IsEmptyString(id, true)){
_error = new Error('name(' + JSON.stringify(name) + ') or id(' + JSON.stringify(id) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _name = name.trim();
let _id = id.trim();
let _url = '/v1/tenant/' + _name;
let _urlargs = 'id=' + _id;
this.startProgress(); // start progressing
this._delete(_url, _urlargs, null, this.tokenHeaderType.unscopedUserToken, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//--------------------------------------------------
// TREE LIST( Role / Resource / Policy Tree List )
//--------------------------------------------------
//
// Common raw method : Get tree in tenant
//
rawGetTreeList(tenant, service, type, path, expand, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let error;
if( r3IsEmptyStringObject(tenant, 'name') ||
(resourceType !== type && roleType !== type && policyType !== type && serviceType !== type) )
{
error = new Error('type(' + JSON.stringify(type) + ') or tenant(' + JSON.stringify(tenant) + ') parameters are wrong.');
console.error(error.message);
callback(error, null);
return;
}
if(r3IsEmptyString(service)){
service = null;
}
let _callback = callback;
let _tenant = tenant.name;
let _path = path;
let _url = '/v1/list/';
if(null !== service){
_url += service + '/';
}
_url += type;
if(!r3IsEmptyString(path)){
_url += '/' + path;
}
let _urlargs = undefined;
if(r3IsSafeTypedEntity(expand, 'boolean')){
_urlargs = 'expand=' + (expand ? 'true' : 'false');
}
this.startProgress(); // start progressing
this._get(_url, _urlargs, null, _tenant, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error, null);
return;
}
if(!r3IsSafeTypedEntity(resobj.children, 'array')){
_callback(null, this.rawCvtTreeListForContainer([], _path));
}else{
_callback(null, this.rawCvtTreeListForContainer(resobj.children, _path));
}
});
}
//
// Common raw method : convert tree list for container's data
//
rawCvtTreeListForContainer(treeList, parentPath)
{
if(!r3IsSafeTypedEntity(treeList, 'array')){
treeList = [];
}
let separator = '';
if(r3IsEmptyString(parentPath)){
parentPath = '';
}else{
separator = '/';
}
for(let cnt = 0; cnt < treeList.length; ++cnt){
treeList[cnt].path = parentPath + separator + treeList[cnt].name;
// reentrant
if(!r3IsSafeTypedEntity(treeList[cnt].children, 'array')){
treeList[cnt].children = [];
}
treeList[cnt].children = this.rawCvtTreeListForContainer(treeList[cnt].children, treeList[cnt].path);
}
return treeList;
}
//
// Get tree in tenant
//
getRoleTreeList(tenant, service, path, expand, callback)
{
this.rawGetTreeList(tenant, service, roleType, path, expand, callback);
}
getResourceTreeList(tenant, service, path, expand, callback)
{
this.rawGetTreeList(tenant, service, resourceType, path, expand, callback);
}
getPolicyTreeList(tenant, service, path, expand, callback)
{
this.rawGetTreeList(tenant, service, policyType, path, expand, callback);
}
getServiceTreeList(tenant, expand, callback)
{
var _tenant = tenant;
var _expand = expand;
var _callback = callback;
this.startProgress(); // start progressing
//
// Get service list for tenant
//
this.rawGetTreeList(_tenant, null, serviceType, null, false, (error, serviceChildren) =>
{
if(null !== error){
console.error('Could not get SERVICE Tree list by ' + error.message);
this.stopProgress(); // stop progressing
_callback(error, null);
return;
}
if(!r3IsSafeTypedEntity(serviceChildren, 'array')){
console.info('SERVICE Tree list is something wrong.');
this.stopProgress(); // stop progressing
_callback(null, []);
return;
}
//
// Check each service for distributed
//
if(0 < serviceChildren.length){
var _tenant2 = _tenant;
var _expand2 = _expand;
var _callback2 = _callback;
var _serviceChildren= serviceChildren;
for(var cnt = 0; cnt < _serviceChildren.length; ++cnt){
((pos) =>
{
if(r3IsEmptyStringObject(_serviceChildren[pos], 'name')){
return;
}
var _pos = pos;
var _callback3 = _callback2;
var _serviceChildren2 = _serviceChildren;
this.rawGetTreeListInServiceTenant(_tenant2, _serviceChildren[pos].name, _expand2, (children, disributed) =>
{
_serviceChildren2[_pos].children = children;
_serviceChildren2[_pos].distributed = disributed;
if((_pos + 1) == _serviceChildren2.length){
this.stopProgress(); // stop progressing
_callback3(null, _serviceChildren2);
}
});
})(cnt);
}
}else{
this.stopProgress(); // stop progressing
_callback(null, serviceChildren);
}
});
}
rawGetTreeListInServiceTenant(tenant, servicename, expand, callback)
{
var _tenant = tenant;
var _servicename= servicename;
var _expand = expand;
var _children = [];
var _callback = callback;
var _distributed= false;
if(r3IsEmptyEntity(_tenant) || r3IsEmptyString(_servicename)){
_callback(_children, _distributed);
return;
}
this.startProgress(); // start progressing
//
// Check role under service+tenant(If it is distributed, the service+tenant is existed)
//
this.getRoleTreeList(_tenant, _servicename, null, _expand, (error, roleChildren) =>
{
if(null !== error){
console.info('Could not get ROLE Tree list in SERVICE+TENANT by ' + error.message);
}
//
// Set distributed flag for service
//
if(null !== error || !r3IsSafeTypedEntity(roleChildren, 'array') || 0 === roleChildren.length){
_distributed = false;
}else{
// Role under service+tenant is existed(distributed).
_distributed = true;
}
if(!_expand || !_distributed){
// Not expand or not distributed
this.stopProgress(); // stop progressing
_callback(_children, _distributed);
return;
}
_children = this.getEmptyTreeList(false);
//
// Set role under service+tenant
//
this.rawSetTreeListChildren(_children, roleType, roleChildren);
//
// Get resource under service+tenant
//
this.getResourceTreeList(_tenant, _servicename, null, _expand, (error, resourceChildren) =>
{
if(null !== error || r3IsEmptyEntity(resourceChildren)){
console.info('Could not get RESOURCE Tree list in SERVICE+TENANT by ' + (null !== error ? error.message : ''));
}else{
this.rawSetTreeListChildren(_children, resourceType, resourceChildren);
}
//
// Get policy under service+tenant
//
this.getPolicyTreeList(_tenant, _servicename, null, _expand, (error, policyChildren) =>
{
if(null !== error || r3IsEmptyEntity(policyChildren)){
console.info('Could not get POLICY Tree list in SERVICE+TENANT by ' + (null !== error ? error.message : ''));
}else{
this.rawSetTreeListChildren(_children, policyType, policyChildren);
}
this.stopProgress(); // stop progressing
_callback(_children, _distributed);
});
});
});
}
//
// Get All Tree List for tenant
//
// array: [
// {
// name: "SERVICE"
// path: "service:",
// children: [
// {
// name: "path",
// path: "full path",
// owner: true,
// distributed:true/false, => the flag for this service is distributed under service+tenant
// children: [ => If service is distributed, this array has ROLE/RESOURCE/POLICY data for service+tenant
// {
// name: "ROLE",
// ...
// },
// {
// name: "RESOURCE",
// ...
// },
// {
// name: "POLICY",
// ...
// }
// ]
// },
// ...
// ]
// },
// {
// name: "ROLE"
// path: "role:",
// children: [
// {
// name: "path",
// path: "full path",
// children: [
// ...
// ]
// },
// ...
// ]
// },
// {
// name: "RESOURCE"
// path: "resource:",
// children: [
// ...
// ]
// },
// {
// name: "POLICY"
// path: "policy:",
// children: [
// ...
// ]
// },
// ]
//
// [NOTE]
// This method always does not return error.
//
getAllTreeList(tenant, callback)
{
let _tenant = tenant;
let _callback = callback;
let _all = this.getEmptyTreeList(true);
if(r3IsEmptyStringObject(tenant, 'name')){
_callback(null, _all);
return;
}
this.startProgress(); // start progressing
this.getServiceTreeList(_tenant, true, (error, serviceChildren) =>
{
if(null !== error){
console.error('Could not get SERVICE Tree list by ' + error.message);
}else{
this.rawSetTreeListChildren(_all, serviceType, serviceChildren);
}
this.getRoleTreeList(_tenant, null, null, true, (error, roleChildren) =>
{
if(null !== error){
console.error('Could not get ROLE Tree list by ' + error.message);
}else{
this.rawSetTreeListChildren(_all, roleType, roleChildren);
}
this.getResourceTreeList(_tenant, null, null, true, (error, resourceChildren) =>
{
if(null !== error){
console.error('Could not get RESOURCE Tree list by ' + error.message);
}else{
this.rawSetTreeListChildren(_all, resourceType, resourceChildren);
}
this.getPolicyTreeList(_tenant, null, null, true, (error, policyChildren) =>
{
if(null !== error){
console.error('Could not get POLICY Tree list by ' + error.message);
}else{
this.rawSetTreeListChildren(_all, policyType, policyChildren);
}
this.stopProgress(); // stop progressing
_callback(null, _all);
});
});
});
});
}
rawSetTreeListChildren(allTreeList, path, children)
{
if(!r3IsSafeTypedEntity(allTreeList, 'array')){
return false;
}
for(let cnt = 0; cnt < allTreeList.length; ++cnt){
if(allTreeList[cnt].path === path){
allTreeList[cnt].children = children;
return true;
}
}
return false;
}
getEmptyTreeList(is_service)
{
let treelist = [];
if(r3IsSafeTypedEntity(is_service, 'boolean') && is_service){
treelist.push({
name: serviceType.toUpperCase(),
path: serviceType,
children: []
});
}
treelist.push({
name: roleType.toUpperCase(),
path: roleType,
children: []
});
treelist.push({
name: resourceType.toUpperCase(),
path: resourceType,
children: []
});
treelist.push({
name: policyType.toUpperCase(),
path: policyType,
children: []
});
return treelist;
}
//
// Select pattern(see: r3container.jsx)
//
// [type] [service] [path] [selected item]
// ROLE/POLICY/RESOURCE empty empty/path ROLE/POLICY/RESOURCE top or path under it
// ROLE/POLICY/RESOURCE service name empty "SERVICE > service name > ROLE/POLICY/RESOURCE"
// ROLE/POLICY/RESOURCE service name path "SERVICE > service name > ROLE/POLICY/RESOURCE > path"
// SERVICE empty empty SERVICE top
// SERVICE service name empty "SERVICE > service name"
//
selectTreeList(treeList, service, type, path)
{
if(!r3IsSafeTypedEntity(treeList, 'array') || 0 === treeList.length){
return false;
}
if(r3IsEmptyString(type, true)){
return false;
}
let cnt;
let cnt2;
if(r3CompareCaseString(serviceType, type)){
//
// This case is under SERVICE
//
if(!r3IsEmptyString(path, true)){
console.error('Wrong parameter: type is SERVICE but specified path(' + path + ')');
return false;
}
// search 'SERVICE' top
for(cnt = 0; cnt < treeList.length; ++cnt){
if(r3IsEmptyStringObject(treeList[cnt], 'path') || !r3CompareCaseString(serviceType, treeList[cnt].path)){
continue; // not target tree
}
// found 'path' == 'service'
// case : select "SERVICE" top
if(r3IsEmptyString(service, true)){
treeList[cnt].selected = true;
return true; // finish
}
// search 'service name' top in children
if(!r3IsSafeTypedEntity(treeList[cnt].children, 'array') || 0 === treeList[cnt].children.length){
continue; // SERVICE does not have children
}
for(cnt2 = 0; cnt2 < treeList[cnt].children.length; ++cnt2){
if(!r3IsEmptyStringObject(treeList[cnt].children[cnt2], 'path') && r3CompareCaseString(service, treeList[cnt].children[cnt2].path)){
// found 'path' == 'service name'
treeList[cnt].children[cnt2].selected = true;
return true; // finish
}
}
}
}else if(r3CompareCaseString(roleType, type) || r3CompareCaseString(policyType, type) || r3CompareCaseString(resourceType, type)){
//
// Case : selected item under ROLE/POLICY/RESOURCE
//
let fullPath = type; // target path is 'ROLE or POLICY or RESOURCE' or 'ROLE or POLICY or RESOURCE'/path...
if(!r3IsEmptyString(path, true)){
fullPath += '/' + path.trim();
}
if(!r3IsEmptyString(service, true)){
//
// The case is under SERVICE
//
// search 'SERVICE' top
for(cnt = 0; cnt < treeList.length; ++cnt){
if(r3IsEmptyStringObject(treeList[cnt], 'path') || !r3CompareCaseString(serviceType, treeList[cnt].path)){
continue; // not target tree
}
// found 'path' == 'service'
// search 'service name' top in children
if(!r3IsSafeTypedEntity(treeList[cnt].children, 'array') || 0 === treeList[cnt].children.length){
continue; // SERVICE does not have children
}
for(cnt2 = 0; cnt2 < treeList[cnt].children.length; ++cnt2){
if(!r3IsEmptyStringObject(treeList[cnt].children[cnt2], 'path') && r3CompareCaseString(service, treeList[cnt].children[cnt2].path)){
// found 'path' == 'service name'
return this.selectSubTreeList(treeList[cnt].children[cnt2].children, fullPath);
}
}
}
}else{
//
// The case is not under SERVICE
//
return this.selectSubTreeList(treeList, fullPath);
}
}else{
console.error('Wrong parameter: unknown type(' + JSON.stringify(type) + ')');
}
return false; // not found
}
selectSubTreeList(subTreeList, path)
{
if(!r3IsSafeTypedEntity(subTreeList, 'array') || 0 === subTreeList.length){
return false;
}
if(r3IsEmptyString(path, true)){
return false;
}
let pos = path.indexOf('/');
let curPath = path;
let childPath = null;
if(0 === pos){
return false;
}else if(0 < pos){
curPath = path.substr(0, pos);
childPath = path.substr(pos + 1);
}
// search current path top
for(let cnt = 0; cnt < subTreeList.length; ++cnt){
if(!r3IsEmptyStringObject(subTreeList[cnt], 'name') && r3CompareCaseString(curPath, subTreeList[cnt].name)){
// found 'name' == current path
if(r3IsEmptyString(childPath, true)){
subTreeList[cnt].selected = true;
return true; // finish
}
// search child path under current path in children
if(!r3IsSafeTypedEntity(subTreeList[cnt].children, 'array') || 0 === subTreeList[cnt].children.length){
continue; // current path does not have children
}
// reentrant
return this.selectSubTreeList(subTreeList[cnt].children, childPath);
}
}
return false;
}
//
// Check service owner
//
checkServiceOwnerInTreeList(treeList, service)
{
if(!r3IsSafeTypedEntity(treeList, 'array') || 0 === treeList.length || r3IsEmptyString(service, true)){
return false;
}
let _service = service.trim();
// search 'SERVICE' top
for(let cnt = 0; cnt < treeList.length; ++cnt){
if(r3IsEmptyStringObject(treeList[cnt], 'path') || !r3CompareCaseString(serviceType, treeList[cnt].path)){
continue; // not target tree
}
// search 'service name' top in children
if(!r3IsSafeTypedEntity(treeList[cnt].children, 'array') || 0 === treeList[cnt].children.length){
continue; // SERVICE does not have children
}
for(let cnt2 = 0; cnt2 < treeList[cnt].children.length; ++cnt2){
if(!r3IsEmptyStringObject(treeList[cnt].children[cnt2], 'path') && r3CompareCaseString(_service, treeList[cnt].children[cnt2].path)){
// found 'path' == 'service name'
if(r3IsSafeTypedEntity(treeList[cnt].children[cnt2].owner, 'boolean') && true === treeList[cnt].children[cnt2].owner){
// service owner is tenant
return true;
}
}
}
}
return false;
}
//
// Check service tenant type
//
checkServiceTenantInTreeList(treeList, service)
{
if(!r3IsSafeTypedEntity(treeList, 'array') || 0 === treeList.length || r3IsEmptyString(service, true)){
return false;
}
let _service = service.trim();
// search 'SERVICE' top
for(let cnt = 0; cnt < treeList.length; ++cnt){
if(r3IsEmptyStringObject(treeList[cnt], 'path') || !r3CompareCaseString(serviceType, treeList[cnt].path)){
continue; // not target tree
}
// search 'service name' top in children
if(!r3IsSafeTypedEntity(treeList[cnt].children, 'array') || 0 === treeList[cnt].children.length){
continue; // SERVICE does not have children
}
for(let cnt2 = 0; cnt2 < treeList[cnt].children.length; ++cnt2){
if(!r3IsEmptyStringObject(treeList[cnt].children[cnt2], 'path') && r3CompareCaseString(_service, treeList[cnt].children[cnt2].path)){
// found 'path' == 'service name'
if(r3IsSafeTypedEntity(treeList[cnt].children[cnt2].children, 'array') && 0 < treeList[cnt].children[cnt2].children.length){
// service has children, thus service is mapped service tenant.
return true;
}
}
}
}
return false;
}
//--------------------------------------------------
// Get Detail information for path
//--------------------------------------------------
getPathDetailInfo(tenant, service, isServiceOwner, hasServiceTenant, type, path)
{
let _tenant = null;
let _service = null;
let _serviceOwner = false;
let _hasServiceTenant = false;
let _type = null;
let _name = null;
let _fullpath = null;
let _currentpath = null;
let _hasParent = false;
let _canCreatePath = false;
let _canCreateService = false;
if(!r3IsEmptyStringObject(tenant, 'name')){
_tenant = tenant;
if(serviceType === type || !r3IsEmptyString(service)){
// [SELECTED TENANT] > SERVICE
_type = type;
// under service
if(r3IsEmptyString(service)){
// [SELECTED TENANT] > SERVICE
_fullpath = 'yrn:yahoo::::service';
_canCreateService = true;
}else if(resourceType === type || roleType === type || policyType === type){
// [SELECTED TENANT] > SERVICE > service > ROLE/POLICY/RESOURCE
_service = service;
_serviceOwner = isServiceOwner;
_fullpath = 'yrn:yahoo:' + _service + '::' + _tenant.name + ':' + _type;
_currentpath = '';
if(!r3IsEmptyString(path)){
// [SELECTED TENANT] > SERVICE > service > ROLE/POLICY/RESOURCE > path
let splitedPath = path.split('/');
if(1 < splitedPath.length){
_hasParent = true;
}
_name = splitedPath[splitedPath.length - 1];
_fullpath += ':' + path;
_currentpath = path;
}
}else{
// [SELECTED TENANT] > SERVICE > service
_service = service;
_serviceOwner = isServiceOwner;
_hasServiceTenant = hasServiceTenant;
_fullpath = 'yrn:yahoo::::service:' + _service;
}
}else if(resourceType === type || roleType === type || policyType === type){
// [SELECTED TENANT] > ROLE/POLICY/RESOURCE
_type = type;
_fullpath = 'yrn:yahoo:::' + _tenant.name + ':' + _type;
_currentpath = '';
if(r3IsEmptyString(path)){
// [SELECTED TENANT] > ROLE/POLICY/RESOURCE
_canCreatePath = true;
}else{
// [SELECTED TENANT] > ROLE/POLICY/RESOURCE > path
let splitedPath = path.split('/');
if(1 < splitedPath.length){
_hasParent = true;
}
_name = splitedPath[splitedPath.length - 1];
_fullpath += ':';
_fullpath += path;
_currentpath = path;
if(resourceType === type || roleType === type){
_canCreatePath = true;
}
}
}else{
// [SELECTED TENANT]
_fullpath = 'yrn:yahoo:';
_fullpath = 'yrn:yahoo:::' + _tenant.name;
_currentpath = '';
}
}
return {
tenant: _tenant,
service: _service,
serviceOwner: _serviceOwner,
hasServiceTenant: _hasServiceTenant,
type: _type,
name: _name,
fullpath: _fullpath,
currentpath: _currentpath,
hasParent: _hasParent,
canCreatePath: _canCreatePath,
canCreateService: _canCreateService
};
}
//--------------------------------------------------
// Common
//--------------------------------------------------
createEmptyData(tenant, type, path, callback)
{
if(roleType === type){
this.createEmptyRoleData(tenant, path, callback);
}else if(policyType === type){
this.createEmptyPolicyData(tenant, path, callback);
}else if(resourceType === type){
this.createEmptyResourceData(tenant, path, callback);
}else{
let error = new Error('create empty data for unknown type(' + JSON.stringify(type) + ')');
console.error(error.message);
callback(error);
}
}
removeData(tenant, type, path, callback)
{
if(roleType === type){
this.removeRoleData(tenant, path, callback);
}else if(policyType === type){
this.removePolicyData(tenant, path, callback);
}else if(resourceType === type){
this.removeResourceData(tenant, path, callback);
}else{
let error = new Error('remove data for unknown type(' + JSON.stringify(type) + ')');
console.error(error.message);
callback(error);
}
}
updateData(tenant, type, path, data, callback)
{
if(roleType === type){
this.updateRoleData(tenant, path, data, true, callback);
}else if(policyType === type){
this.updatePolicyData(tenant, path, data, callback);
}else if(resourceType === type){
this.updateResourceData(tenant, path, data, callback);
}else{
let error = new Error('update data for unknown type(' + JSON.stringify(type) + ')');
console.error(error.message);
callback(error);
}
}
//--------------------------------------------------
// Service
//--------------------------------------------------
//
// Get Service data
//
// tenant : tenant name
// servicename : service name
//
getServiceData(tenant, servicename, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyStringObject(tenant, 'name') || r3IsEmptyString(servicename, true)){
_error = new Error('tenant(' + JSON.stringify(tenant) + ') or service(' + JSON.stringify(servicename) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _tenant = tenant.name;
let _service = servicename.trim();
let _url = '/v1/service/' + _service;
this.startProgress(); // start progressing
this._get(_url, null, null, _tenant, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error, null);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error, null);
return;
}
if(r3IsEmptyEntity(resobj.service)){
_callback(null, null);
}else{
_callback(null, resobj.service);
}
});
}
//
// Update Service data
//
// tenant : tenant name
// servicename : service name
// tenants : adding tenant name(s), this value means following:
// undefined do not set any new tenants
// null do not set any new tenants(but if clear_tenant is true, remove all tenants without owner)
// string set one new tenant(if clear_tenant is true, remove existing tenants without owner)
// array set some new tenant(if clear_tenant is true, remove existing tenants without owner)
// clear_tenant : value is true, it means remove tenants without "tenants"
// verify : verify url/object, if undefined/null, verify is not update.
//
updateServiceData(tenant, servicename, tenants, clear_tenant, verify, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _error;
let _callback = callback;
if(r3IsEmptyStringObject(tenant, 'name') || r3IsEmptyString(servicename, true)){
_error = new Error('tenant(' + JSON.stringify(tenant) + ') or service(' + JSON.stringify(servicename) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _service = servicename.trim();
let _tenants = null;
let _is_clear = false;
let _verify = null;
if(undefined !== tenants){
if(r3IsSafeTypedEntity(clear_tenant, 'boolean') && true === clear_tenant){
_is_clear = true;
}
if(r3IsSafeTypedEntity(tenants, 'array') && 0 < tenants.length){
_tenants = tenants;
}else if(!r3IsEmptyString(tenants)){
_tenants = [tenants];
}
}
if(!r3IsEmptyEntity(verify)){
_verify = verify;
}
if(null === _tenants && false === _is_clear && null === _verify){
_error = new Error('Nothing to update for service');
console.error(_error.message);
_callback(_error);
return;
}
let _tenant = tenant.name;
let _url = '/v1/service/' + _service;
let _urlargs = undefined;
if(r3IsSafeTypedEntity(_tenants, 'array') && 0 < _tenants.length){
_urlargs = 'tenant=' + JSON.stringify(_tenants);
}
if(_is_clear){
if(undefined !== _urlargs){
_urlargs += '&';
}else{
_urlargs = '';
}
_urlargs += 'clear_tenant=true';
}
if(null !== _verify){
if(undefined !== _urlargs){
_urlargs += '&';
}else{
_urlargs = '';
}
_urlargs += 'verify=' + JSON.stringify(_verify);
}
this.startProgress(); // start progressing
this._put(_url, _urlargs, null, _tenant, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//
// Create Initialized Service
//
// tenant : tenant name
// servicename : service name
// verify : verify url or static resource data object
//
createInitializedService(tenant, servicename, verify, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyStringObject(tenant, 'name') || r3IsEmptyString(servicename, true) || r3IsEmptyEntity(verify) || (r3IsSafeTypedEntity(verify, 'string') && r3IsEmptyString(verify, true)) ){
_error = new Error('tenant(' + JSON.stringify(tenant) + ') or service(' + JSON.stringify(servicename) + ') or verify(' + JSON.stringify(verify) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _tenant = tenant.name;
let _service = servicename.trim();
let _verify = verify.trim();
let _url = '/v1/service';
let _urlargs = 'name=' + _service + '&verify=' + JSON.stringify(_verify);
this.startProgress(); // start progressing
this._put(_url, _urlargs, null, _tenant, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//
// Remove Service or Service tenant
//
// tenant : tenant name
// servicename : service name
//
removeService(tenant, servicename, isServiceTenant, callback)
{
if(!r3IsSafeTypedEntity(callback, 'function')){
console.error('callback parameter is wrong.');
return;
}
let _callback = callback;
let _error;
if(r3IsEmptyStringObject(tenant, 'name') || r3IsEmptyString(servicename, true)){
_error = new Error('tenant(' + JSON.stringify(tenant) + ') or service(' + JSON.stringify(servicename) + ') parameters are wrong.');
console.error(_error.message);
_callback(_error);
return;
}
let _tenant = tenant.name;
let _service = servicename.trim();
let _url = '/v1/service/' + _service;
let _urlargs = undefined;
if(r3IsSafeTypedEntity(isServiceTenant, 'boolean') && true === isServiceTenant){
_urlargs = 'tenant=' + tenant.name;
}
this.startProgress(); // start progressing
this._delete(_url, _urlargs, null, _tenant, (error, resobj) =>
{
this.stopProgress(); // stop progressing
if(null !== error){
console.error(error.message);
_callback(error);
return;
}
if(true !== resobj.result){
error = new Error(resobj.message);
console.error(error.message);
_callback(error);
return;
}
_callback(null);
});
}
//
// Utility: Get error string which is result of verifying service resource
//
getErrorServiceResourceVerify(serviceResource)
{
let checkResult = checkServiceResourceValue(serviceResource);
let result = null;
if(null != checkResult.error){
i