k2hr3-api 1.0.42 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/k2hr3-init.sh.templ +4 -4
- package/dist/.gitkeep +0 -0
- package/dist/src/app.js +262 -0
- package/{bin → dist/src/bin}/run.sh +1 -1
- package/dist/src/bin/watcher.js +113 -0
- package/dist/src/bin/www.js +217 -0
- package/dist/src/lib/basicipcheck.js +392 -0
- package/dist/src/lib/cacerts.js +106 -0
- package/dist/src/lib/dbglogging.js +190 -0
- package/dist/src/lib/dummyuserapi.js +719 -0
- package/dist/src/lib/ipwatch.js +354 -0
- package/dist/src/lib/k2hr3acrutil.js +532 -0
- package/dist/src/lib/k2hr3apiutil.js +1444 -0
- package/dist/src/lib/k2hr3cliutil.js +183 -0
- package/dist/src/lib/k2hr3config.js +832 -0
- package/dist/src/lib/k2hr3cryptutil.js +258 -0
- package/dist/src/lib/k2hr3dkc.js +12121 -0
- package/dist/src/lib/k2hr3extdata.js +198 -0
- package/dist/src/lib/k2hr3keys.js +207 -0
- package/dist/src/lib/k2hr3resutil.js +111 -0
- package/dist/src/lib/k2hr3template.js +6546 -0
- package/dist/src/lib/k2hr3tokens.js +2643 -0
- package/dist/src/lib/k2hr3userdata.js +296 -0
- package/dist/src/lib/k8soidc.js +1000 -0
- package/dist/src/lib/openstackapiv2.js +695 -0
- package/dist/src/lib/openstackapiv3.js +932 -0
- package/dist/src/lib/openstackep.js +667 -0
- package/{tests/auto_common.js → dist/src/lib/types.js} +4 -38
- package/dist/src/routes/acr.js +704 -0
- package/dist/src/routes/debugVerify.js +294 -0
- package/dist/src/routes/extdata.js +219 -0
- package/dist/src/routes/list.js +264 -0
- package/dist/src/routes/policy.js +840 -0
- package/dist/src/routes/resource.js +1489 -0
- package/dist/src/routes/role.js +2627 -0
- package/dist/src/routes/service.js +908 -0
- package/dist/src/routes/tenant.js +1141 -0
- package/dist/src/routes/userTokens.js +482 -0
- package/dist/src/routes/userdata.js +212 -0
- package/dist/src/routes/version.js +103 -0
- package/package.json +152 -121
- package/ChangeLog +0 -378
- package/app.js +0 -292
- package/bin/watcher +0 -122
- package/bin/www +0 -180
- package/eslint.config.mjs +0 -68
- package/lib/basicipcheck.js +0 -376
- package/lib/cacerts.js +0 -71
- package/lib/dbglogging.js +0 -151
- package/lib/dummyuserapi.js +0 -766
- package/lib/ipwatch.js +0 -379
- package/lib/k2hr3acrutil.js +0 -516
- package/lib/k2hr3apiutil.js +0 -1494
- package/lib/k2hr3cliutil.js +0 -191
- package/lib/k2hr3config.js +0 -826
- package/lib/k2hr3cryptutil.js +0 -254
- package/lib/k2hr3dkc.js +0 -12632
- package/lib/k2hr3extdata.js +0 -198
- package/lib/k2hr3keys.js +0 -234
- package/lib/k2hr3resutil.js +0 -100
- package/lib/k2hr3template.js +0 -6925
- package/lib/k2hr3tokens.js +0 -2799
- package/lib/k2hr3userdata.js +0 -312
- package/lib/k8soidc.js +0 -1012
- package/lib/openstackapiv2.js +0 -764
- package/lib/openstackapiv3.js +0 -1032
- package/lib/openstackep.js +0 -553
- package/routes/acr.js +0 -738
- package/routes/debugVerify.js +0 -263
- package/routes/extdata.js +0 -232
- package/routes/list.js +0 -270
- package/routes/policy.js +0 -869
- package/routes/resource.js +0 -1441
- package/routes/role.js +0 -2664
- package/routes/service.js +0 -894
- package/routes/tenant.js +0 -1095
- package/routes/userTokens.js +0 -511
- package/routes/userdata.js +0 -218
- package/routes/version.js +0 -108
- package/templ/Dockerfile.templ +0 -71
- package/tests/auto_acr.js +0 -1101
- package/tests/auto_acr_spec.js +0 -79
- package/tests/auto_all_spec.js +0 -142
- package/tests/auto_control_subprocess.sh +0 -243
- package/tests/auto_extdata.js +0 -220
- package/tests/auto_extdata_spec.js +0 -79
- package/tests/auto_init_config_json.sh +0 -275
- package/tests/auto_k2hdkc_server.ini +0 -109
- package/tests/auto_k2hdkc_slave.ini +0 -83
- package/tests/auto_list.js +0 -439
- package/tests/auto_list_spec.js +0 -79
- package/tests/auto_policy.js +0 -1579
- package/tests/auto_policy_spec.js +0 -79
- package/tests/auto_resource.js +0 -10956
- package/tests/auto_resource_spec.js +0 -79
- package/tests/auto_role.js +0 -6150
- package/tests/auto_role_spec.js +0 -79
- package/tests/auto_service.js +0 -770
- package/tests/auto_service_spec.js +0 -79
- package/tests/auto_subprocesses.js +0 -114
- package/tests/auto_template.sh +0 -126
- package/tests/auto_tenant.js +0 -1100
- package/tests/auto_tenant_spec.js +0 -79
- package/tests/auto_token_util.js +0 -219
- package/tests/auto_userdata.js +0 -292
- package/tests/auto_userdata_spec.js +0 -79
- package/tests/auto_usertokens.js +0 -565
- package/tests/auto_usertokens_spec.js +0 -79
- package/tests/auto_version.js +0 -127
- package/tests/auto_version_spec.js +0 -79
- package/tests/auto_watcher.js +0 -157
- package/tests/auto_watcher_spec.js +0 -79
- package/tests/k2hdkc_test.data +0 -986
- package/tests/k2hdkc_test_load.sh +0 -255
- package/tests/k2hr3template_test.js +0 -187
- package/tests/k2hr3template_test.sh +0 -339
- package/tests/k2hr3template_test_async.js +0 -216
- package/tests/k2hr3template_test_template.result +0 -7117
- package/tests/k2hr3template_test_template.txt +0 -3608
- package/tests/k2hr3template_test_vars.js +0 -194
- package/tests/manual_acr_delete.js +0 -143
- package/tests/manual_acr_get.js +0 -297
- package/tests/manual_acr_postput.js +0 -215
- package/tests/manual_allusertenant_get.js +0 -113
- package/tests/manual_extdata_get.js +0 -191
- package/tests/manual_k2hr3keys_get.js +0 -84
- package/tests/manual_list_gethead.js +0 -230
- package/tests/manual_policy_delete.js +0 -132
- package/tests/manual_policy_gethead.js +0 -275
- package/tests/manual_policy_postput.js +0 -297
- package/tests/manual_resource_delete.js +0 -433
- package/tests/manual_resource_gethead.js +0 -423
- package/tests/manual_resource_postput.js +0 -487
- package/tests/manual_role_delete.js +0 -404
- package/tests/manual_role_gethead.js +0 -547
- package/tests/manual_role_postput.js +0 -544
- package/tests/manual_service_delete.js +0 -153
- package/tests/manual_service_gethead.js +0 -178
- package/tests/manual_service_postput.js +0 -348
- package/tests/manual_tenant_delete.js +0 -186
- package/tests/manual_tenant_gethead.js +0 -268
- package/tests/manual_tenant_postput.js +0 -293
- package/tests/manual_test.sh +0 -352
- package/tests/manual_userdata_get.js +0 -173
- package/tests/manual_usertoken_gethead.js +0 -136
- package/tests/manual_usertoken_postput.js +0 -310
- package/tests/manual_version_get.js +0 -127
- package/tests/run_local_test_k2hdkc.sh +0 -174
- package/tests/test.sh +0 -333
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* K2HR3 REST API
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2017 Yahoo Japan Corporation.
|
|
6
|
+
*
|
|
7
|
+
* K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
|
|
8
|
+
* common management information for the cloud.
|
|
9
|
+
* K2HR3 can dynamically manage information as "who", "what", "operate".
|
|
10
|
+
* These are stored as roles, resources, policies in K2hdkc, and the
|
|
11
|
+
* client system can dynamically read and modify these information.
|
|
12
|
+
*
|
|
13
|
+
* For the full copyright and license information, please view
|
|
14
|
+
* the license file that was distributed with this source code.
|
|
15
|
+
*
|
|
16
|
+
* AUTHOR: Takeshi Nakatani
|
|
17
|
+
* CREATE: Wed Jun 8 2017
|
|
18
|
+
* REVISION:
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
55
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.openstackapiv3 = void 0;
|
|
59
|
+
const https = __importStar(require("https"));
|
|
60
|
+
const http = __importStar(require("http"));
|
|
61
|
+
const k2hr3apiutil_1 = __importDefault(require("./k2hr3apiutil"));
|
|
62
|
+
const dbglogging_1 = __importDefault(require("./dbglogging"));
|
|
63
|
+
const openstackep_1 = __importDefault(require("./openstackep")); // OpenStack KeyStone EndPoint(=osksep)
|
|
64
|
+
const cacerts_1 = require("./cacerts");
|
|
65
|
+
//
|
|
66
|
+
// Get Unscoped token by user name and passwd from Openstack identity v3 API
|
|
67
|
+
//
|
|
68
|
+
// Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail
|
|
69
|
+
//
|
|
70
|
+
// Request: {
|
|
71
|
+
// "auth": {
|
|
72
|
+
// "identity": {
|
|
73
|
+
// "password": {
|
|
74
|
+
// "user": {
|
|
75
|
+
// "domain": {
|
|
76
|
+
// "id": "default"
|
|
77
|
+
// },
|
|
78
|
+
// "password": "*********",
|
|
79
|
+
// "name": "*********"
|
|
80
|
+
// }
|
|
81
|
+
// },
|
|
82
|
+
// "methods": ["password"]
|
|
83
|
+
// }
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
//
|
|
87
|
+
// Response:
|
|
88
|
+
// Header "X-Subject-Token: 16e4638398574f1d9364............" (*1)
|
|
89
|
+
// Body {
|
|
90
|
+
// "token": {
|
|
91
|
+
// "audit_ids": [
|
|
92
|
+
// "################....."
|
|
93
|
+
// ],
|
|
94
|
+
// "expires_at": "2017-06-17T00:20:38.863072Z", (*2)
|
|
95
|
+
// "extras": {},
|
|
96
|
+
// "issued_at": "2017-06-16T00:20:38.863143Z",
|
|
97
|
+
// "methods": [
|
|
98
|
+
// "password"
|
|
99
|
+
// ],
|
|
100
|
+
// "user": {
|
|
101
|
+
// "domain": {
|
|
102
|
+
// "id": "default",
|
|
103
|
+
// "name": "Default"
|
|
104
|
+
// },
|
|
105
|
+
// "id": "*****************************...", (*4)
|
|
106
|
+
// "name": "user name" (*3)
|
|
107
|
+
// }
|
|
108
|
+
// }
|
|
109
|
+
// }
|
|
110
|
+
//
|
|
111
|
+
// callback(error, result):
|
|
112
|
+
// result = {
|
|
113
|
+
// user: user name (*3)
|
|
114
|
+
// userid: user id (*4)
|
|
115
|
+
// scoped: false (always false)
|
|
116
|
+
// token: token string(id) (*1)
|
|
117
|
+
// expire: expire string (*2)
|
|
118
|
+
// region: region string (region name for keystone endpoint)
|
|
119
|
+
// token_seed: seed ({publisher: 'OPENSTACKV3'})
|
|
120
|
+
// }
|
|
121
|
+
//
|
|
122
|
+
const rawGetUserUnscopedTokenV3 = (uname, passwd, callback) => {
|
|
123
|
+
if (!k2hr3apiutil_1.default.isSafeString(uname)) {
|
|
124
|
+
const error = new Error('some parameters are wrong : uname=' + JSON.stringify(uname));
|
|
125
|
+
dbglogging_1.default.elog(error.message);
|
|
126
|
+
callback(error, null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!k2hr3apiutil_1.default.isSafeString(passwd)) {
|
|
130
|
+
passwd = null;
|
|
131
|
+
}
|
|
132
|
+
const _uname = uname;
|
|
133
|
+
const _passwd = passwd;
|
|
134
|
+
const _callback = callback;
|
|
135
|
+
// get end points for keystone
|
|
136
|
+
openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => {
|
|
137
|
+
if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) {
|
|
138
|
+
const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : ''));
|
|
139
|
+
dbglogging_1.default.elog(error.message);
|
|
140
|
+
_callback(error, null);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// got safe endpoint for keystone
|
|
144
|
+
//r3logger.dlog(keystone_ep);
|
|
145
|
+
// build parameters for request
|
|
146
|
+
const body = {
|
|
147
|
+
'auth': {
|
|
148
|
+
'identity': {
|
|
149
|
+
'password': {
|
|
150
|
+
'user': {
|
|
151
|
+
'domain': {
|
|
152
|
+
'id': 'default'
|
|
153
|
+
},
|
|
154
|
+
'password': _passwd,
|
|
155
|
+
'name': _uname
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
'methods': ['password']
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http;
|
|
163
|
+
const strbody = JSON.stringify(body);
|
|
164
|
+
const headers = {
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
'Content-Length': strbody.length
|
|
167
|
+
};
|
|
168
|
+
const options = {
|
|
169
|
+
'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname),
|
|
170
|
+
'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0,
|
|
171
|
+
'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens',
|
|
172
|
+
'method': 'POST',
|
|
173
|
+
'headers': headers,
|
|
174
|
+
'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined
|
|
175
|
+
};
|
|
176
|
+
// send request
|
|
177
|
+
const req = agent.request(options, (res) => {
|
|
178
|
+
let body = '';
|
|
179
|
+
const status = res.statusCode;
|
|
180
|
+
const headers = res.headers;
|
|
181
|
+
dbglogging_1.default.dlog('response status: ' + res.statusCode);
|
|
182
|
+
dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers));
|
|
183
|
+
res.setEncoding('utf8');
|
|
184
|
+
res.on('data', (chunk) => {
|
|
185
|
+
//r3logger.dlog('response chunk: ' + chunk);
|
|
186
|
+
body += chunk;
|
|
187
|
+
});
|
|
188
|
+
res.on('end', () => {
|
|
189
|
+
if (300 <= (status ?? 500)) {
|
|
190
|
+
const error = new Error('could not get unscoped token by status=' + String(status ?? 500));
|
|
191
|
+
dbglogging_1.default.elog(error.message);
|
|
192
|
+
_callback(error, null);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) {
|
|
196
|
+
const error = new Error('could not find unscoped token in header(X-Subject-Token)');
|
|
197
|
+
dbglogging_1.default.elog(error.message);
|
|
198
|
+
_callback(error, null);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']);
|
|
202
|
+
//r3logger.dlog('response body: ' + body);
|
|
203
|
+
let res_body = body;
|
|
204
|
+
if (k2hr3apiutil_1.default.checkSimpleJSON(body)) {
|
|
205
|
+
res_body = JSON.parse(body);
|
|
206
|
+
}
|
|
207
|
+
if (!k2hr3apiutil_1.default.isPlainObject(res_body) ||
|
|
208
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token) ||
|
|
209
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) ||
|
|
210
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token.user) ||
|
|
211
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) ||
|
|
212
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.name) ||
|
|
213
|
+
!k2hr3apiutil_1.default.compareCaseString(res_body.token.user.name, _uname)) {
|
|
214
|
+
const error = new Error('could not get unscoped token by something wrong response body : ' + body);
|
|
215
|
+
dbglogging_1.default.elog(error.message);
|
|
216
|
+
_callback(error, null);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// convert openstack user id(16 bytes hex string) to UUID(not UUID4)
|
|
220
|
+
const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16));
|
|
221
|
+
// build result
|
|
222
|
+
const resobj = {
|
|
223
|
+
user: res_body.token.user.name.toLowerCase(),
|
|
224
|
+
userid: user_id_uuid4,
|
|
225
|
+
scoped: false,
|
|
226
|
+
token: tmpToken,
|
|
227
|
+
expire: k2hr3apiutil_1.default.getSafeString(res_body.token.expires_at),
|
|
228
|
+
region: k2hr3apiutil_1.default.getSafeString(keystone_ep.region).toLowerCase(),
|
|
229
|
+
token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' })
|
|
230
|
+
};
|
|
231
|
+
_callback(null, resobj);
|
|
232
|
+
return;
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
req.on('error', (exception) => {
|
|
236
|
+
dbglogging_1.default.elog('problem with request: ' + exception.message);
|
|
237
|
+
_callback(exception, null);
|
|
238
|
+
return;
|
|
239
|
+
});
|
|
240
|
+
// write data to request body
|
|
241
|
+
req.write(strbody);
|
|
242
|
+
req.end();
|
|
243
|
+
}, true);
|
|
244
|
+
};
|
|
245
|
+
//
|
|
246
|
+
// Get Unscoped token by openstack token from Openstack identity v3 API
|
|
247
|
+
//
|
|
248
|
+
// Document: https://docs.openstack.org/api-ref/identity/v3/?expanded=token-authentication-with-unscoped-authorization-detail#token-authentication-with-unscoped-authorization
|
|
249
|
+
//
|
|
250
|
+
// Request: {
|
|
251
|
+
// "auth": {
|
|
252
|
+
// "identity": {
|
|
253
|
+
// "methods": [
|
|
254
|
+
// "token"
|
|
255
|
+
// ],
|
|
256
|
+
// "token": {
|
|
257
|
+
// "id": "**********"
|
|
258
|
+
// }
|
|
259
|
+
// }
|
|
260
|
+
// }
|
|
261
|
+
// }
|
|
262
|
+
//
|
|
263
|
+
//
|
|
264
|
+
// Response:
|
|
265
|
+
// Header "X-Subject-Token: 16e4638398574f1d9364............" (*1)
|
|
266
|
+
// {
|
|
267
|
+
// "token": {
|
|
268
|
+
// "audit_ids": [
|
|
269
|
+
// "################....."
|
|
270
|
+
// ],
|
|
271
|
+
// "expires_at": "2017-06-17T00:20:38.863072Z", (*2)
|
|
272
|
+
// "issued_at": "2015-11-05T21:00:33.819948Z",
|
|
273
|
+
// "methods": [
|
|
274
|
+
// "token"
|
|
275
|
+
// ],
|
|
276
|
+
// "user": {
|
|
277
|
+
// "domain": {
|
|
278
|
+
// "id": "default",
|
|
279
|
+
// "name": "Default"
|
|
280
|
+
// },
|
|
281
|
+
// "id": "*****************************...", (*4)
|
|
282
|
+
// "name": "user name" (*3)
|
|
283
|
+
// "password_expires_at": null
|
|
284
|
+
// }
|
|
285
|
+
// }
|
|
286
|
+
// }
|
|
287
|
+
//
|
|
288
|
+
// callback(error, result):
|
|
289
|
+
// result = {
|
|
290
|
+
// user: user name (*3)
|
|
291
|
+
// userid: user id (*4)
|
|
292
|
+
// scoped: false (always false)
|
|
293
|
+
// token: token string(id) (*1)
|
|
294
|
+
// expire: expire string (*2)
|
|
295
|
+
// region: region string (region name for keystone endpoint)
|
|
296
|
+
// token_seed: seed ({publisher: 'OPENSTACKV3'})
|
|
297
|
+
// }
|
|
298
|
+
//
|
|
299
|
+
const rawGetUserUnscopedTokenByOstokenV3 = (ostoken, callback) => {
|
|
300
|
+
if (!k2hr3apiutil_1.default.isSafeString(ostoken)) {
|
|
301
|
+
const error = new Error('some parameter is wrong : ostoken=' + JSON.stringify(ostoken));
|
|
302
|
+
dbglogging_1.default.elog(error.message);
|
|
303
|
+
callback(error, null);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const _ostoken = ostoken;
|
|
307
|
+
const _callback = callback;
|
|
308
|
+
// get end points for keystone
|
|
309
|
+
openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => {
|
|
310
|
+
if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) {
|
|
311
|
+
const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : ''));
|
|
312
|
+
dbglogging_1.default.elog(error.message);
|
|
313
|
+
_callback(error, null);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// got safe endpoint for keystone
|
|
317
|
+
//r3logger.dlog(keystone_ep);
|
|
318
|
+
// build parameters for request
|
|
319
|
+
const body = {
|
|
320
|
+
'auth': {
|
|
321
|
+
'identity': {
|
|
322
|
+
'token': {
|
|
323
|
+
'id': _ostoken,
|
|
324
|
+
},
|
|
325
|
+
'methods': ['token']
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http;
|
|
330
|
+
const strbody = JSON.stringify(body);
|
|
331
|
+
const headers = {
|
|
332
|
+
'Content-Type': 'application/json',
|
|
333
|
+
'Content-Length': strbody.length
|
|
334
|
+
};
|
|
335
|
+
const options = {
|
|
336
|
+
'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname),
|
|
337
|
+
'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0,
|
|
338
|
+
'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens',
|
|
339
|
+
'method': 'POST',
|
|
340
|
+
'headers': headers,
|
|
341
|
+
'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined
|
|
342
|
+
};
|
|
343
|
+
// send request
|
|
344
|
+
const req = agent.request(options, (res) => {
|
|
345
|
+
let body = '';
|
|
346
|
+
const status = res.statusCode;
|
|
347
|
+
const headers = res.headers;
|
|
348
|
+
dbglogging_1.default.dlog('response status: ' + res.statusCode);
|
|
349
|
+
dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers));
|
|
350
|
+
res.setEncoding('utf8');
|
|
351
|
+
res.on('data', (chunk) => {
|
|
352
|
+
//r3logger.dlog('response chunk: ' + chunk);
|
|
353
|
+
body += chunk;
|
|
354
|
+
});
|
|
355
|
+
res.on('end', () => {
|
|
356
|
+
if (300 <= (status ?? 500)) {
|
|
357
|
+
const error = new Error('could not get unscoped token by status=' + String(status ?? 500));
|
|
358
|
+
dbglogging_1.default.elog(error.message);
|
|
359
|
+
_callback(error, null);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) {
|
|
363
|
+
const error = new Error('could not find unscoped token in header(X-Subject-Token)');
|
|
364
|
+
dbglogging_1.default.elog(error.message);
|
|
365
|
+
_callback(error, null);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']);
|
|
369
|
+
//r3logger.dlog('response body: ' + body);
|
|
370
|
+
let res_body = body;
|
|
371
|
+
if (k2hr3apiutil_1.default.checkSimpleJSON(body)) {
|
|
372
|
+
res_body = JSON.parse(body);
|
|
373
|
+
}
|
|
374
|
+
if (!k2hr3apiutil_1.default.isPlainObject(res_body) ||
|
|
375
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token) ||
|
|
376
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) ||
|
|
377
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token.user) ||
|
|
378
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) ||
|
|
379
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.name)) {
|
|
380
|
+
const error = new Error('could not get unscoped token by something wrong response body : ' + body);
|
|
381
|
+
dbglogging_1.default.elog(error.message);
|
|
382
|
+
_callback(error, null);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
// convert openstack user id(16 bytes hex string) to UUID(not UUID4)
|
|
386
|
+
const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16));
|
|
387
|
+
// build result
|
|
388
|
+
const resobj = {
|
|
389
|
+
user: res_body.token.user.name.toLowerCase(),
|
|
390
|
+
userid: user_id_uuid4,
|
|
391
|
+
scoped: false,
|
|
392
|
+
token: tmpToken,
|
|
393
|
+
expire: k2hr3apiutil_1.default.getSafeString(res_body.token.expires_at),
|
|
394
|
+
region: k2hr3apiutil_1.default.getSafeString(keystone_ep.region).toLowerCase(),
|
|
395
|
+
token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' })
|
|
396
|
+
};
|
|
397
|
+
_callback(null, resobj);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
req.on('error', (exception) => {
|
|
401
|
+
dbglogging_1.default.elog('problem with request: ' + exception.message);
|
|
402
|
+
_callback(exception, null);
|
|
403
|
+
return;
|
|
404
|
+
});
|
|
405
|
+
// write data to request body
|
|
406
|
+
req.write(strbody);
|
|
407
|
+
req.end();
|
|
408
|
+
}, true);
|
|
409
|
+
};
|
|
410
|
+
//
|
|
411
|
+
// Get Scoped token by unscoped token and tenant name from Openstack identity v3 API
|
|
412
|
+
//
|
|
413
|
+
// Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=token-authentication-with-scoped-authorization-detail
|
|
414
|
+
//
|
|
415
|
+
// Request: {
|
|
416
|
+
// "auth": {
|
|
417
|
+
// "identity": {
|
|
418
|
+
// "methods": [
|
|
419
|
+
// "token"
|
|
420
|
+
// ],
|
|
421
|
+
// "token": {
|
|
422
|
+
// "id": "<unscoped token>"
|
|
423
|
+
// }
|
|
424
|
+
// },
|
|
425
|
+
// "scope": {
|
|
426
|
+
// "project": {
|
|
427
|
+
// "id": "<project(tenant) id>"
|
|
428
|
+
// }
|
|
429
|
+
// }
|
|
430
|
+
// }
|
|
431
|
+
// }
|
|
432
|
+
//
|
|
433
|
+
// Response:
|
|
434
|
+
// Header "X-Subject-Token: 16e4638398574f1d9364............" (*1)
|
|
435
|
+
// Body {
|
|
436
|
+
// "token": {
|
|
437
|
+
// "audit_ids": [
|
|
438
|
+
// "######################",
|
|
439
|
+
// "######################"
|
|
440
|
+
// ],
|
|
441
|
+
// "catalog": [
|
|
442
|
+
// {
|
|
443
|
+
// "endpoints": [
|
|
444
|
+
// {
|
|
445
|
+
// "id": "*****************************...",
|
|
446
|
+
// "interface": "public" (=public, others are admin/internal),
|
|
447
|
+
// "region": "***************", (*5)
|
|
448
|
+
// "region_id": ###############
|
|
449
|
+
// "url": "http://keystone/v2.0"
|
|
450
|
+
// },
|
|
451
|
+
// ...
|
|
452
|
+
// ],
|
|
453
|
+
// "id": "*****************************...",
|
|
454
|
+
// "name": "keystone",
|
|
455
|
+
// "type": "identity" (=identity)
|
|
456
|
+
// },
|
|
457
|
+
// ...
|
|
458
|
+
// ],
|
|
459
|
+
// "expires_at": "2017-06-17T00:46:16.923305Z", (*2)
|
|
460
|
+
// "extras": {},
|
|
461
|
+
// "issued_at": "2017-06-16T01:07:56.325351Z",
|
|
462
|
+
// "methods": [
|
|
463
|
+
// "token",
|
|
464
|
+
// "password"
|
|
465
|
+
// ],
|
|
466
|
+
// "project": {
|
|
467
|
+
// "domain": {
|
|
468
|
+
// "id": "default",
|
|
469
|
+
// "name": "Default"
|
|
470
|
+
// },
|
|
471
|
+
// "id": "*****************************...", (=project id)
|
|
472
|
+
// "name": "project name" (*5: project(tenant) name)
|
|
473
|
+
// },
|
|
474
|
+
// "roles": [
|
|
475
|
+
// {
|
|
476
|
+
// "id": "*****************************...",
|
|
477
|
+
// "name": "_member_"
|
|
478
|
+
// }
|
|
479
|
+
// ],
|
|
480
|
+
// "user": {
|
|
481
|
+
// "domain": {
|
|
482
|
+
// "id": "default",
|
|
483
|
+
// "name": "Default"
|
|
484
|
+
// },
|
|
485
|
+
// "id": "*****************************...", (*4: user id)
|
|
486
|
+
// "name": "user name" (*3: user name)
|
|
487
|
+
// }
|
|
488
|
+
// }
|
|
489
|
+
// }
|
|
490
|
+
//
|
|
491
|
+
// callback(error, result):
|
|
492
|
+
// result = {
|
|
493
|
+
// user: user name (*3)
|
|
494
|
+
// userid: user id (*4)
|
|
495
|
+
// scoped: true (always true)
|
|
496
|
+
// token: token string(id) (*1)
|
|
497
|
+
// expire: expire string (*2)
|
|
498
|
+
// region: region string (identity's *5 for region name)
|
|
499
|
+
// token_seed: seed ({publisher: 'OPENSTACKV3'})
|
|
500
|
+
// }
|
|
501
|
+
//
|
|
502
|
+
const rawGetUserScopedTokenV3 = (unscopedtoken, tenantid, callback) => {
|
|
503
|
+
if (!k2hr3apiutil_1.default.isSafeString(unscopedtoken)) {
|
|
504
|
+
const error = new Error('parameter is wrong : unscopedtoken=' + JSON.stringify(unscopedtoken));
|
|
505
|
+
dbglogging_1.default.elog(error.message);
|
|
506
|
+
callback(error, null);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (!k2hr3apiutil_1.default.isSafeNumeric(tenantid)) {
|
|
510
|
+
// id allows hex character string, decimal character string, decimal number value.
|
|
511
|
+
//
|
|
512
|
+
const error = new Error('parameter is wrong : tenantid=' + JSON.stringify(tenantid));
|
|
513
|
+
dbglogging_1.default.elog(error.message);
|
|
514
|
+
callback(error, null);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (!k2hr3apiutil_1.default.isSafeString(tenantid)) {
|
|
518
|
+
// to string
|
|
519
|
+
tenantid = String(tenantid);
|
|
520
|
+
}
|
|
521
|
+
const _unscopedtoken = unscopedtoken;
|
|
522
|
+
const _tenantid = tenantid;
|
|
523
|
+
const _callback = callback;
|
|
524
|
+
// get end points for keystone
|
|
525
|
+
openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => {
|
|
526
|
+
if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) {
|
|
527
|
+
const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : ''));
|
|
528
|
+
dbglogging_1.default.elog(error.message);
|
|
529
|
+
_callback(error, null);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
// got safe endpoint for keystone
|
|
533
|
+
//r3logger.dlog(keystone_ep);
|
|
534
|
+
// build parameters for request
|
|
535
|
+
const body = {
|
|
536
|
+
'auth': {
|
|
537
|
+
'identity': {
|
|
538
|
+
'methods': [
|
|
539
|
+
'token'
|
|
540
|
+
],
|
|
541
|
+
'token': {
|
|
542
|
+
'id': _unscopedtoken
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
'scope': {
|
|
546
|
+
'project': {
|
|
547
|
+
'id': _tenantid
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http;
|
|
553
|
+
const strbody = JSON.stringify(body);
|
|
554
|
+
const headers = {
|
|
555
|
+
'Content-Type': 'application/json',
|
|
556
|
+
'Content-Length': strbody.length
|
|
557
|
+
};
|
|
558
|
+
const options = {
|
|
559
|
+
'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname),
|
|
560
|
+
'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0,
|
|
561
|
+
'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/auth/tokens',
|
|
562
|
+
'method': 'POST',
|
|
563
|
+
'headers': headers,
|
|
564
|
+
'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined
|
|
565
|
+
};
|
|
566
|
+
// send request
|
|
567
|
+
const req = agent.request(options, (res) => {
|
|
568
|
+
let body = '';
|
|
569
|
+
const status = res.statusCode;
|
|
570
|
+
const headers = res.headers;
|
|
571
|
+
dbglogging_1.default.dlog('response status: ' + res.statusCode);
|
|
572
|
+
dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers));
|
|
573
|
+
res.setEncoding('utf8');
|
|
574
|
+
res.on('data', (chunk) => {
|
|
575
|
+
//r3logger.dlog('response chunk: ' + chunk);
|
|
576
|
+
body += chunk;
|
|
577
|
+
});
|
|
578
|
+
res.on('end', () => {
|
|
579
|
+
if (300 <= (status ?? 500)) {
|
|
580
|
+
const error = new Error('could not get scoped token by status=' + String(status ?? 500));
|
|
581
|
+
dbglogging_1.default.elog(error.message);
|
|
582
|
+
_callback(error, null);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (!k2hr3apiutil_1.default.isPlainObject(headers) || !k2hr3apiutil_1.default.isSafeString(headers['x-subject-token'])) {
|
|
586
|
+
const error = new Error('could not find unscoped token in header(X-Subject-Token)');
|
|
587
|
+
dbglogging_1.default.elog(error.message);
|
|
588
|
+
_callback(error, null);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const tmpToken = k2hr3apiutil_1.default.getSafeString(headers['x-subject-token']);
|
|
592
|
+
//r3logger.dlog('response body: ' + body);
|
|
593
|
+
let res_body = body;
|
|
594
|
+
if (k2hr3apiutil_1.default.checkSimpleJSON(body)) {
|
|
595
|
+
res_body = JSON.parse(body);
|
|
596
|
+
}
|
|
597
|
+
if (!k2hr3apiutil_1.default.isPlainObject(res_body) ||
|
|
598
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token) ||
|
|
599
|
+
!k2hr3apiutil_1.default.isNotEmptyArray(res_body.token.catalog) ||
|
|
600
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.expires_at) ||
|
|
601
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token.project) ||
|
|
602
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.project.id) ||
|
|
603
|
+
!k2hr3apiutil_1.default.compareCaseString(res_body.token.project.id, _tenantid) ||
|
|
604
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.project.name) ||
|
|
605
|
+
!k2hr3apiutil_1.default.isPlainObject(res_body.token.user) ||
|
|
606
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.id) ||
|
|
607
|
+
!k2hr3apiutil_1.default.isSafeString(res_body.token.user.name)) {
|
|
608
|
+
const error = new Error('could not get scoped token by something wrong response body : ' + body);
|
|
609
|
+
dbglogging_1.default.elog(error.message);
|
|
610
|
+
_callback(error, null);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
// check & get region
|
|
614
|
+
let region = null;
|
|
615
|
+
for (let cnt = 0; cnt < res_body.token.catalog.length && null === region; ++cnt) {
|
|
616
|
+
const tmpCatalog = res_body.token.catalog[cnt];
|
|
617
|
+
if (!k2hr3apiutil_1.default.isPlainObject(tmpCatalog) ||
|
|
618
|
+
!k2hr3apiutil_1.default.isNotEmptyArray(tmpCatalog.endpoints) ||
|
|
619
|
+
!k2hr3apiutil_1.default.isSafeString(tmpCatalog.type)) {
|
|
620
|
+
dbglogging_1.default.wlog('one of response for scoped token is something wrong : ' + JSON.stringify(tmpCatalog));
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
// target region by type=identity
|
|
624
|
+
if (!k2hr3apiutil_1.default.compareCaseString('identity', tmpCatalog.type)) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
// check region
|
|
628
|
+
for (let cnt2 = 0; cnt2 < tmpCatalog.endpoints.length; ++cnt2) {
|
|
629
|
+
const tmpEp = tmpCatalog.endpoints[cnt2];
|
|
630
|
+
if (k2hr3apiutil_1.default.isPlainObject(tmpEp) &&
|
|
631
|
+
k2hr3apiutil_1.default.isSafeString(tmpEp.interface) &&
|
|
632
|
+
k2hr3apiutil_1.default.compareCaseString('public', tmpEp.interface) &&
|
|
633
|
+
k2hr3apiutil_1.default.isSafeString(tmpEp.region)) {
|
|
634
|
+
if (k2hr3apiutil_1.default.compareCaseString(tmpEp.region, keystone_ep.region)) {
|
|
635
|
+
// found
|
|
636
|
+
region = tmpEp.region;
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
dbglogging_1.default.wlog('unknown region(' + tmpEp.region + '), we need to find region(' + keystone_ep.region + '), so skip this');
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
dbglogging_1.default.dlog('one of response endpoint for scoped token is not target or something wrong : ' + JSON.stringify(tmpEp));
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (!k2hr3apiutil_1.default.isSafeString(region)) {
|
|
649
|
+
const error = new Error('could not find request region in result.');
|
|
650
|
+
dbglogging_1.default.elog(error.message);
|
|
651
|
+
_callback(error, null);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
// convert openstack user id(16 bytes hex string) to UUID(not UUID4)
|
|
655
|
+
const user_id_uuid4 = k2hr3apiutil_1.default.getSafeString(k2hr3apiutil_1.default.cvtNumberStringToUuid4(res_body.token.user.id, 16));
|
|
656
|
+
// build result
|
|
657
|
+
const resobj = {
|
|
658
|
+
user: res_body.token.user.name.toLowerCase(),
|
|
659
|
+
userid: user_id_uuid4,
|
|
660
|
+
scoped: false,
|
|
661
|
+
token: tmpToken,
|
|
662
|
+
expire: res_body.token.expires_at,
|
|
663
|
+
region: region.toLowerCase(),
|
|
664
|
+
token_seed: JSON.stringify({ publisher: 'OPENSTACKV3' })
|
|
665
|
+
};
|
|
666
|
+
_callback(null, resobj);
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
req.on('error', (exception) => {
|
|
670
|
+
dbglogging_1.default.elog('problem with request: ' + exception.message);
|
|
671
|
+
_callback(exception, null);
|
|
672
|
+
return;
|
|
673
|
+
});
|
|
674
|
+
// write data to request body
|
|
675
|
+
req.write(strbody);
|
|
676
|
+
req.end();
|
|
677
|
+
}, true);
|
|
678
|
+
};
|
|
679
|
+
//
|
|
680
|
+
// Verify User Token for OpenStack V3
|
|
681
|
+
//
|
|
682
|
+
// user : target user name for token
|
|
683
|
+
// token : check token
|
|
684
|
+
// token_seed : token seed data
|
|
685
|
+
//
|
|
686
|
+
// result : {
|
|
687
|
+
// result: true/false
|
|
688
|
+
// message: null or error message string
|
|
689
|
+
// }
|
|
690
|
+
//
|
|
691
|
+
const rawVerifyUserTokenPublisherV3 = (token_seed) => {
|
|
692
|
+
if (!k2hr3apiutil_1.default.isSafeString(token_seed)) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
// parse seed
|
|
696
|
+
if (!k2hr3apiutil_1.default.checkSimpleJSON(token_seed)) {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
const seed = k2hr3apiutil_1.default.parseJSON(token_seed);
|
|
700
|
+
if (!k2hr3apiutil_1.default.isValTypeTokenSeed(seed)) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
if (!k2hr3apiutil_1.default.isSafeString(seed.publisher) ||
|
|
704
|
+
(seed.publisher != 'OPENSTACKV3')) // publisher must be 'OPENSTACKV3'
|
|
705
|
+
{
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
return true;
|
|
709
|
+
};
|
|
710
|
+
const rawWrapVerifyUserTokenPublisherV3 = (token_seed) => {
|
|
711
|
+
const resobj = { result: true, message: null };
|
|
712
|
+
if (!rawVerifyUserTokenPublisherV3(token_seed)) {
|
|
713
|
+
resobj.result = false;
|
|
714
|
+
resobj.message = 'token_seed(not printable) is not safe entity.';
|
|
715
|
+
dbglogging_1.default.elog(resobj.message);
|
|
716
|
+
return resobj;
|
|
717
|
+
}
|
|
718
|
+
return resobj;
|
|
719
|
+
};
|
|
720
|
+
const rawVerifyUserTokenV3 = (user, token, token_seed) => {
|
|
721
|
+
const resobj = { result: true, message: null };
|
|
722
|
+
if (!k2hr3apiutil_1.default.isSafeString(user) || !k2hr3apiutil_1.default.isSafeString(token) || !k2hr3apiutil_1.default.isSafeString(token_seed)) {
|
|
723
|
+
resobj.result = false;
|
|
724
|
+
resobj.message = 'some parameters are wrong : token=' + JSON.stringify(token) + ', token_seed=<not printable>, user=' + JSON.stringify(user);
|
|
725
|
+
dbglogging_1.default.elog(resobj.message);
|
|
726
|
+
return resobj;
|
|
727
|
+
}
|
|
728
|
+
// check seed
|
|
729
|
+
if (!rawVerifyUserTokenPublisherV3(token_seed)) {
|
|
730
|
+
resobj.result = false;
|
|
731
|
+
resobj.message = 'token_seed(not printable) is not safe entity.';
|
|
732
|
+
dbglogging_1.default.elog(resobj.message);
|
|
733
|
+
return resobj;
|
|
734
|
+
}
|
|
735
|
+
return resobj;
|
|
736
|
+
};
|
|
737
|
+
//
|
|
738
|
+
// Get tenant list by unscoped token from Openstack identity v3 API
|
|
739
|
+
//
|
|
740
|
+
// Document: https://developer.openstack.org/api-ref/identity/v3/?expanded=list-projects-detail
|
|
741
|
+
//
|
|
742
|
+
// Request: X-Auth-Token: <unscoped token>
|
|
743
|
+
// https://keystone/v3/users/<user id>/projects
|
|
744
|
+
//
|
|
745
|
+
// Response: {
|
|
746
|
+
// "links": {
|
|
747
|
+
// "next": null,
|
|
748
|
+
// "previous": null,
|
|
749
|
+
// "self": "https://keystone/v3/users/<user id>/projects"
|
|
750
|
+
// },
|
|
751
|
+
// "projects": [
|
|
752
|
+
// {
|
|
753
|
+
// "description": null, (*1)
|
|
754
|
+
// "domain_id": "default",
|
|
755
|
+
// "enabled": true,
|
|
756
|
+
// "id": "<project id>", (*2)
|
|
757
|
+
// "is_domain": false,
|
|
758
|
+
// "links": {
|
|
759
|
+
// "self": "https://keystone/v3/projects/<project id>"
|
|
760
|
+
// },
|
|
761
|
+
// "name": "******", (*3)
|
|
762
|
+
// "parent_id": null
|
|
763
|
+
// },
|
|
764
|
+
// ...
|
|
765
|
+
// ]
|
|
766
|
+
// }
|
|
767
|
+
//
|
|
768
|
+
// callback(error, result):
|
|
769
|
+
// result = [
|
|
770
|
+
// {
|
|
771
|
+
// name: project(tenant) name (*3)
|
|
772
|
+
// id: project(tenant) id (*2)
|
|
773
|
+
// description: project(tenant) description (*1)
|
|
774
|
+
// display: display name (*3)
|
|
775
|
+
// },
|
|
776
|
+
// ...
|
|
777
|
+
// ]
|
|
778
|
+
//
|
|
779
|
+
// [TODO]
|
|
780
|
+
// Should not we use "tenant id" instead of "tenant name" for "name"?
|
|
781
|
+
// We need this consideration.
|
|
782
|
+
//
|
|
783
|
+
const rawGetUserTenantListV3 = (unscopedtoken, userid, callback) => {
|
|
784
|
+
if (!k2hr3apiutil_1.default.isSafeString(unscopedtoken) || !k2hr3apiutil_1.default.isSafeString(userid)) {
|
|
785
|
+
const error = new Error('parameter is wrong : unscopedtoken=' + JSON.stringify(unscopedtoken) + ', userid=' + JSON.stringify(userid));
|
|
786
|
+
dbglogging_1.default.elog(error.message);
|
|
787
|
+
callback(error, null);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const _unscopedtoken = unscopedtoken;
|
|
791
|
+
const _callback = callback;
|
|
792
|
+
// convert user id(UUID) to openstack user id(16 bytes hex string)
|
|
793
|
+
const _bin_userid = k2hr3apiutil_1.default.cvtStrToBinUuid4(userid);
|
|
794
|
+
if (null == _bin_userid) {
|
|
795
|
+
const error = new Error('parameter is wrong : userid=' + JSON.stringify(userid));
|
|
796
|
+
dbglogging_1.default.elog(error.message);
|
|
797
|
+
callback(error, null);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const _userid = _bin_userid.toString('hex');
|
|
801
|
+
// get end points for keystone
|
|
802
|
+
openstackep_1.default.getKeystoneEndpoint((err, keystone_ep) => {
|
|
803
|
+
if (k2hr3apiutil_1.default.isSafeEntity(err) || !k2hr3apiutil_1.default.isPlainObject(keystone_ep)) {
|
|
804
|
+
const error = new Error('could not get keystone end point by ' + (k2hr3apiutil_1.default.isSafeEntity(err) ? k2hr3apiutil_1.default.getSafeString(err.message) : ''));
|
|
805
|
+
dbglogging_1.default.elog(error.message);
|
|
806
|
+
_callback(error, null);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
// got safe endpoint for keystone
|
|
810
|
+
//r3logger.dlog(keystone_ep);
|
|
811
|
+
// build parameters for request
|
|
812
|
+
const agent = k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) ? https : http;
|
|
813
|
+
const headers = {
|
|
814
|
+
'Content-Type': 'application/json',
|
|
815
|
+
'X-Auth-Token': _unscopedtoken,
|
|
816
|
+
'Content-Length': 0
|
|
817
|
+
};
|
|
818
|
+
const options = {
|
|
819
|
+
'host': k2hr3apiutil_1.default.getSafeString(keystone_ep.hostname),
|
|
820
|
+
'port': k2hr3apiutil_1.default.isSafeNumber(keystone_ep.port) ? keystone_ep.port : 0,
|
|
821
|
+
'path': k2hr3apiutil_1.default.getSafeString(keystone_ep.pathname) + '/v3/users/' + _userid + '/projects',
|
|
822
|
+
'method': 'GET',
|
|
823
|
+
'headers': headers,
|
|
824
|
+
'ca': (k2hr3apiutil_1.default.compareCaseString('https:', keystone_ep.protocol) && null !== cacerts_1.ca) ? ((0, cacerts_1.ca)() ?? undefined) : undefined
|
|
825
|
+
};
|
|
826
|
+
// send request
|
|
827
|
+
const req = agent.request(options, (res) => {
|
|
828
|
+
let body = '';
|
|
829
|
+
const status = res.statusCode;
|
|
830
|
+
dbglogging_1.default.dlog('response status: ' + res.statusCode);
|
|
831
|
+
dbglogging_1.default.dlog('response header: ' + JSON.stringify(res.headers));
|
|
832
|
+
res.setEncoding('utf8');
|
|
833
|
+
res.on('data', (chunk) => {
|
|
834
|
+
//r3logger.dlog('response chunk: ' + chunk);
|
|
835
|
+
body += chunk;
|
|
836
|
+
});
|
|
837
|
+
res.on('end', () => {
|
|
838
|
+
if (300 <= (status ?? 500)) {
|
|
839
|
+
const error = new Error('could not get scoped token by status=' + String(status ?? 500));
|
|
840
|
+
dbglogging_1.default.elog(error.message);
|
|
841
|
+
_callback(error, null);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
//r3logger.dlog('response body: ' + body);
|
|
845
|
+
let res_body = body;
|
|
846
|
+
if (k2hr3apiutil_1.default.checkSimpleJSON(body)) {
|
|
847
|
+
res_body = JSON.parse(body);
|
|
848
|
+
}
|
|
849
|
+
if (!k2hr3apiutil_1.default.isPlainObject(res_body) ||
|
|
850
|
+
!k2hr3apiutil_1.default.isNotEmptyArray(res_body.projects)) {
|
|
851
|
+
const error = new Error('failed to get project(tenant) list by unscoped token.');
|
|
852
|
+
dbglogging_1.default.elog(error.message);
|
|
853
|
+
_callback(error, null);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
// convert result array
|
|
857
|
+
const resobj = [];
|
|
858
|
+
for (let cnt = 0; cnt < res_body.projects.length; ++cnt) {
|
|
859
|
+
const tmpProject = res_body.projects[cnt];
|
|
860
|
+
if (!k2hr3apiutil_1.default.isPlainObject(tmpProject) ||
|
|
861
|
+
!k2hr3apiutil_1.default.isSafeString(tmpProject.id) ||
|
|
862
|
+
!k2hr3apiutil_1.default.isSafeString(tmpProject.name)) {
|
|
863
|
+
dbglogging_1.default.wlog('one of response for project(tenant) list is something wrong : ' + JSON.stringify(tmpProject));
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const tenant = {
|
|
867
|
+
name: tmpProject.name.toLowerCase(),
|
|
868
|
+
id: tmpProject.id.toLowerCase(),
|
|
869
|
+
description: k2hr3apiutil_1.default.getSafeString(tmpProject.description),
|
|
870
|
+
display: tmpProject.name
|
|
871
|
+
};
|
|
872
|
+
resobj.push(tenant);
|
|
873
|
+
}
|
|
874
|
+
if (0 === resobj.length) {
|
|
875
|
+
const error = new Error('could not get any projects(tenant) list by unscoped token.');
|
|
876
|
+
dbglogging_1.default.elog(error.message);
|
|
877
|
+
_callback(error, null);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
_callback(null, resobj);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
req.on('error', (exception) => {
|
|
884
|
+
dbglogging_1.default.elog('problem with request: ' + exception.message);
|
|
885
|
+
callback(exception, null);
|
|
886
|
+
return;
|
|
887
|
+
});
|
|
888
|
+
}, true);
|
|
889
|
+
};
|
|
890
|
+
//---------------------------------------------------------
|
|
891
|
+
// Exports
|
|
892
|
+
//---------------------------------------------------------
|
|
893
|
+
exports.openstackapiv3 = {
|
|
894
|
+
getUserUnscopedToken: rawGetUserUnscopedTokenV3,
|
|
895
|
+
//
|
|
896
|
+
// token : unscoped/scoped token for openstack
|
|
897
|
+
//
|
|
898
|
+
getUserUnscopedTokenByToken: rawGetUserUnscopedTokenByOstokenV3,
|
|
899
|
+
//
|
|
900
|
+
// tenantname : not used
|
|
901
|
+
// tenantid : for keystone v3 api
|
|
902
|
+
//
|
|
903
|
+
getUserScopedToken: rawGetUserScopedTokenV3,
|
|
904
|
+
//
|
|
905
|
+
// Verify seed publisher type
|
|
906
|
+
//
|
|
907
|
+
verifyUserTokenPublisher: rawWrapVerifyUserTokenPublisherV3,
|
|
908
|
+
//
|
|
909
|
+
// Verify token
|
|
910
|
+
//
|
|
911
|
+
// tenant is not used.
|
|
912
|
+
//
|
|
913
|
+
verifyUserToken: (dkcobj_permanent, user, tenant, token, token_seed) => {
|
|
914
|
+
return rawVerifyUserTokenV3(user, token, token_seed);
|
|
915
|
+
},
|
|
916
|
+
//
|
|
917
|
+
// userid : for keystone v3 api
|
|
918
|
+
//
|
|
919
|
+
getUserTenantList: rawGetUserTenantListV3
|
|
920
|
+
};
|
|
921
|
+
//
|
|
922
|
+
// Default
|
|
923
|
+
//
|
|
924
|
+
exports.default = exports.openstackapiv3;
|
|
925
|
+
/*
|
|
926
|
+
* Local variables:
|
|
927
|
+
* tab-width: 4
|
|
928
|
+
* c-basic-offset: 4
|
|
929
|
+
* End:
|
|
930
|
+
* vim600: noexpandtab sw=4 ts=4 fdm=marker
|
|
931
|
+
* vim<600: noexpandtab sw=4 ts=4
|
|
932
|
+
*/
|