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.
Files changed (149) hide show
  1. package/config/k2hr3-init.sh.templ +4 -4
  2. package/dist/.gitkeep +0 -0
  3. package/dist/src/app.js +262 -0
  4. package/{bin → dist/src/bin}/run.sh +1 -1
  5. package/dist/src/bin/watcher.js +113 -0
  6. package/dist/src/bin/www.js +217 -0
  7. package/dist/src/lib/basicipcheck.js +392 -0
  8. package/dist/src/lib/cacerts.js +106 -0
  9. package/dist/src/lib/dbglogging.js +190 -0
  10. package/dist/src/lib/dummyuserapi.js +719 -0
  11. package/dist/src/lib/ipwatch.js +354 -0
  12. package/dist/src/lib/k2hr3acrutil.js +532 -0
  13. package/dist/src/lib/k2hr3apiutil.js +1444 -0
  14. package/dist/src/lib/k2hr3cliutil.js +183 -0
  15. package/dist/src/lib/k2hr3config.js +832 -0
  16. package/dist/src/lib/k2hr3cryptutil.js +258 -0
  17. package/dist/src/lib/k2hr3dkc.js +12121 -0
  18. package/dist/src/lib/k2hr3extdata.js +198 -0
  19. package/dist/src/lib/k2hr3keys.js +207 -0
  20. package/dist/src/lib/k2hr3resutil.js +111 -0
  21. package/dist/src/lib/k2hr3template.js +6546 -0
  22. package/dist/src/lib/k2hr3tokens.js +2643 -0
  23. package/dist/src/lib/k2hr3userdata.js +296 -0
  24. package/dist/src/lib/k8soidc.js +1000 -0
  25. package/dist/src/lib/openstackapiv2.js +695 -0
  26. package/dist/src/lib/openstackapiv3.js +932 -0
  27. package/dist/src/lib/openstackep.js +667 -0
  28. package/{tests/auto_common.js → dist/src/lib/types.js} +4 -38
  29. package/dist/src/routes/acr.js +704 -0
  30. package/dist/src/routes/debugVerify.js +294 -0
  31. package/dist/src/routes/extdata.js +219 -0
  32. package/dist/src/routes/list.js +264 -0
  33. package/dist/src/routes/policy.js +840 -0
  34. package/dist/src/routes/resource.js +1489 -0
  35. package/dist/src/routes/role.js +2627 -0
  36. package/dist/src/routes/service.js +908 -0
  37. package/dist/src/routes/tenant.js +1141 -0
  38. package/dist/src/routes/userTokens.js +482 -0
  39. package/dist/src/routes/userdata.js +212 -0
  40. package/dist/src/routes/version.js +103 -0
  41. package/package.json +152 -121
  42. package/ChangeLog +0 -378
  43. package/app.js +0 -292
  44. package/bin/watcher +0 -122
  45. package/bin/www +0 -180
  46. package/eslint.config.mjs +0 -68
  47. package/lib/basicipcheck.js +0 -376
  48. package/lib/cacerts.js +0 -71
  49. package/lib/dbglogging.js +0 -151
  50. package/lib/dummyuserapi.js +0 -766
  51. package/lib/ipwatch.js +0 -379
  52. package/lib/k2hr3acrutil.js +0 -516
  53. package/lib/k2hr3apiutil.js +0 -1494
  54. package/lib/k2hr3cliutil.js +0 -191
  55. package/lib/k2hr3config.js +0 -826
  56. package/lib/k2hr3cryptutil.js +0 -254
  57. package/lib/k2hr3dkc.js +0 -12632
  58. package/lib/k2hr3extdata.js +0 -198
  59. package/lib/k2hr3keys.js +0 -234
  60. package/lib/k2hr3resutil.js +0 -100
  61. package/lib/k2hr3template.js +0 -6925
  62. package/lib/k2hr3tokens.js +0 -2799
  63. package/lib/k2hr3userdata.js +0 -312
  64. package/lib/k8soidc.js +0 -1012
  65. package/lib/openstackapiv2.js +0 -764
  66. package/lib/openstackapiv3.js +0 -1032
  67. package/lib/openstackep.js +0 -553
  68. package/routes/acr.js +0 -738
  69. package/routes/debugVerify.js +0 -263
  70. package/routes/extdata.js +0 -232
  71. package/routes/list.js +0 -270
  72. package/routes/policy.js +0 -869
  73. package/routes/resource.js +0 -1441
  74. package/routes/role.js +0 -2664
  75. package/routes/service.js +0 -894
  76. package/routes/tenant.js +0 -1095
  77. package/routes/userTokens.js +0 -511
  78. package/routes/userdata.js +0 -218
  79. package/routes/version.js +0 -108
  80. package/templ/Dockerfile.templ +0 -71
  81. package/tests/auto_acr.js +0 -1101
  82. package/tests/auto_acr_spec.js +0 -79
  83. package/tests/auto_all_spec.js +0 -142
  84. package/tests/auto_control_subprocess.sh +0 -243
  85. package/tests/auto_extdata.js +0 -220
  86. package/tests/auto_extdata_spec.js +0 -79
  87. package/tests/auto_init_config_json.sh +0 -275
  88. package/tests/auto_k2hdkc_server.ini +0 -109
  89. package/tests/auto_k2hdkc_slave.ini +0 -83
  90. package/tests/auto_list.js +0 -439
  91. package/tests/auto_list_spec.js +0 -79
  92. package/tests/auto_policy.js +0 -1579
  93. package/tests/auto_policy_spec.js +0 -79
  94. package/tests/auto_resource.js +0 -10956
  95. package/tests/auto_resource_spec.js +0 -79
  96. package/tests/auto_role.js +0 -6150
  97. package/tests/auto_role_spec.js +0 -79
  98. package/tests/auto_service.js +0 -770
  99. package/tests/auto_service_spec.js +0 -79
  100. package/tests/auto_subprocesses.js +0 -114
  101. package/tests/auto_template.sh +0 -126
  102. package/tests/auto_tenant.js +0 -1100
  103. package/tests/auto_tenant_spec.js +0 -79
  104. package/tests/auto_token_util.js +0 -219
  105. package/tests/auto_userdata.js +0 -292
  106. package/tests/auto_userdata_spec.js +0 -79
  107. package/tests/auto_usertokens.js +0 -565
  108. package/tests/auto_usertokens_spec.js +0 -79
  109. package/tests/auto_version.js +0 -127
  110. package/tests/auto_version_spec.js +0 -79
  111. package/tests/auto_watcher.js +0 -157
  112. package/tests/auto_watcher_spec.js +0 -79
  113. package/tests/k2hdkc_test.data +0 -986
  114. package/tests/k2hdkc_test_load.sh +0 -255
  115. package/tests/k2hr3template_test.js +0 -187
  116. package/tests/k2hr3template_test.sh +0 -339
  117. package/tests/k2hr3template_test_async.js +0 -216
  118. package/tests/k2hr3template_test_template.result +0 -7117
  119. package/tests/k2hr3template_test_template.txt +0 -3608
  120. package/tests/k2hr3template_test_vars.js +0 -194
  121. package/tests/manual_acr_delete.js +0 -143
  122. package/tests/manual_acr_get.js +0 -297
  123. package/tests/manual_acr_postput.js +0 -215
  124. package/tests/manual_allusertenant_get.js +0 -113
  125. package/tests/manual_extdata_get.js +0 -191
  126. package/tests/manual_k2hr3keys_get.js +0 -84
  127. package/tests/manual_list_gethead.js +0 -230
  128. package/tests/manual_policy_delete.js +0 -132
  129. package/tests/manual_policy_gethead.js +0 -275
  130. package/tests/manual_policy_postput.js +0 -297
  131. package/tests/manual_resource_delete.js +0 -433
  132. package/tests/manual_resource_gethead.js +0 -423
  133. package/tests/manual_resource_postput.js +0 -487
  134. package/tests/manual_role_delete.js +0 -404
  135. package/tests/manual_role_gethead.js +0 -547
  136. package/tests/manual_role_postput.js +0 -544
  137. package/tests/manual_service_delete.js +0 -153
  138. package/tests/manual_service_gethead.js +0 -178
  139. package/tests/manual_service_postput.js +0 -348
  140. package/tests/manual_tenant_delete.js +0 -186
  141. package/tests/manual_tenant_gethead.js +0 -268
  142. package/tests/manual_tenant_postput.js +0 -293
  143. package/tests/manual_test.sh +0 -352
  144. package/tests/manual_userdata_get.js +0 -173
  145. package/tests/manual_usertoken_gethead.js +0 -136
  146. package/tests/manual_usertoken_postput.js +0 -310
  147. package/tests/manual_version_get.js +0 -127
  148. package/tests/run_local_test_k2hdkc.sh +0 -174
  149. package/tests/test.sh +0 -333
@@ -0,0 +1,1000 @@
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: Hirotaka Wakabayashi
17
+ * CREATE: Fri, Aug 20 2021
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.k8soidc = void 0;
59
+ //------------------------------------------------------------------------
60
+ // Usage
61
+ //------------------------------------------------------------------------
62
+ // To enable this module, make the following settings in the K2HR3 API
63
+ // configuration file(ex, production.json/local.json/etc).
64
+ //
65
+ // {
66
+ // 'keystone': {
67
+ // 'type': 'k8soidc'
68
+ // }
69
+ // }
70
+ //
71
+ // Set the value of the 'keystone'->'type' object to 'k8soidc'.
72
+ //
73
+ // Next, this module requires its own information, so the following
74
+ // settings in configuration file are required.
75
+ //
76
+ // {
77
+ // 'k8soidc': {
78
+ // 'audience': '<client id for open id connect>',
79
+ // 'issuer': '<issue url for open id connect>',
80
+ // 'usernamekey': '<user name key name in token>',
81
+ // 'k8sapi_url': '<kubernetes api url>',
82
+ // 'k8s_ca_path': '<CA cert file path for kubernetes api url>',
83
+ // 'k8s_sa_token': '<Service account token for kubernetes>'
84
+ // 'unscopedtoken_exp':'<Expire limit for unscoped Token created from oidc>'
85
+ // }
86
+ // }
87
+ //
88
+ // Set the 'k8soidc' object as above. This object should contain the
89
+ // following keys(objects). The contents of each setting are explained.
90
+ //
91
+ // [audience]
92
+ // Set the client id for Open id connect. This key and value are
93
+ // required.
94
+ // [issuer]
95
+ // Set the issuer URL of Open id connect. This key and value are
96
+ // required.
97
+ // [usernamekey]
98
+ // Specify the key name that is the Username set in the Token of
99
+ // Open id connect. If there is no key representing Username in
100
+ // Token, it can be omitted. If omitted, the value of the 'sub'
101
+ // key is treated as the Username.
102
+ // [k8sapi_url]
103
+ // Specify the URL of the Kubernetes API. This module accesses
104
+ // the Kubernetes API to get the list of Kubernetes Namespaces.
105
+ // For example, that is 'https://kubernetes.default.svc'. This key
106
+ // and value are required.
107
+ // [k8s_ca_path]
108
+ // Specify the path of the CA certificate to access the Kubernetes
109
+ // API. If you're running the K2HR3 API inside a Kubernetes pod,
110
+ // it's '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'.
111
+ // This key and value are required.
112
+ // [k8s_sa_token]
113
+ // Specify the Token of the Service Account to access the Kubernetes
114
+ // API. If you're running the K2HR3 API inside a Kubernetes pod,
115
+ // it's '/var/run/secrets/kubernetes.io/serviceaccount/token'.
116
+ // This key and value are required.
117
+ // [unscopedtoken_exp]
118
+ // Specifies the expiration date of the Unscoped token created by
119
+ // OIDC. This value is specified in seconds(s).
120
+ // If this value does not exist or is less than or equal to 0,
121
+ // the default value will be used. The default value is the same
122
+ // as the OIDC token expiration date.
123
+ //
124
+ //------------------------------------------------------------------------
125
+ const k2hr3apiutil_1 = __importDefault(require("./k2hr3apiutil"));
126
+ const k2hr3dkc_1 = __importDefault(require("./k2hr3dkc"));
127
+ const dbglogging_1 = __importDefault(require("./dbglogging"));
128
+ const fs = __importStar(require("fs"));
129
+ const https = __importStar(require("https"));
130
+ const k2hr3keys_1 = require("./k2hr3keys");
131
+ const client_node_1 = require("@kubernetes/client-node");
132
+ const jose_1 = require("jose");
133
+ const { decode } = jose_1.base64url;
134
+ const k2hr3config_1 = require("./k2hr3config");
135
+ const apiConf = new k2hr3config_1.r3ApiConfig();
136
+ //---------------------------------------------------------
137
+ // Variables and Initializer
138
+ //---------------------------------------------------------
139
+ //
140
+ // Const Variables
141
+ //
142
+ const K8S_PUBLISHER_NAME = 'K8SOIDC';
143
+ const K8S_REGION_NAME = 'K8sCluster';
144
+ const OIDC_JWKS_URI_KEYNAME = 'jwks_uri';
145
+ //
146
+ // Global variables from configuration file
147
+ //
148
+ let oidc_audience = null;
149
+ let oidc_issuer = null;
150
+ let oidc_username = null;
151
+ let k8s_api_url = null;
152
+ let k8s_ca_cert = null;
153
+ let k2hr3_k8s_sa_token = null;
154
+ let unscopedtoken_exp = 0; // Expire limit for unscoped Token created from oidc(default is 0 means as same as oidc limit)
155
+ (() => {
156
+ const tmp_oidc_config = apiConf.getOtherObject('k8soidc');
157
+ if (k2hr3apiutil_1.default.isPlainObject(tmp_oidc_config)) {
158
+ oidc_audience = k2hr3apiutil_1.default.isString(tmp_oidc_config.audience) ? tmp_oidc_config.audience : null;
159
+ oidc_issuer = k2hr3apiutil_1.default.isString(tmp_oidc_config.issuer) ? tmp_oidc_config.issuer : null;
160
+ oidc_username = k2hr3apiutil_1.default.isString(tmp_oidc_config.usernamekey) ? tmp_oidc_config.usernamekey : null;
161
+ k8s_api_url = k2hr3apiutil_1.default.isString(tmp_oidc_config.k8sapi_url) ? tmp_oidc_config.k8sapi_url : null;
162
+ k8s_ca_cert = k2hr3apiutil_1.default.isString(tmp_oidc_config.k8s_ca_path) ? tmp_oidc_config.k8s_ca_path : null;
163
+ try {
164
+ k2hr3_k8s_sa_token = fs.readFileSync(k2hr3apiutil_1.default.getSafeString(tmp_oidc_config.k8s_sa_token), 'utf8');
165
+ }
166
+ catch {
167
+ k2hr3_k8s_sa_token = null;
168
+ }
169
+ // unscopedtoken_exp must be number
170
+ if (k2hr3apiutil_1.default.isSafeNumber(tmp_oidc_config.unscopedtoken_exp) && 0 < tmp_oidc_config.unscopedtoken_exp) {
171
+ unscopedtoken_exp = tmp_oidc_config.unscopedtoken_exp;
172
+ }
173
+ }
174
+ })();
175
+ //---------------------------------------------------------
176
+ // User Token for k8s oidc
177
+ //---------------------------------------------------------
178
+ //
179
+ // user : user name which is verified authentication
180
+ // user_id : user id which is verified authentication
181
+ // expire_limit : specify expire second(default 24H = 24 * 60 * 60 sec), and allow empty
182
+ //
183
+ // result : {
184
+ // result: true/false
185
+ // message: null or error message string
186
+ // token: undefined(error) or user token string
187
+ // expire_at: expire date(UTC ISO 8601)
188
+ // token_seed: JSON token seed data
189
+ // userid: user id
190
+ // }
191
+ //
192
+ // [NOTE]
193
+ // user token seed value is following
194
+ // {
195
+ // publisher: "K8SOIDC"
196
+ // userexid: "user extra id(a part of seed uuid4)"
197
+ // date: "UTC ISO 8601 time at create"
198
+ // expire: "UTC ISO 8601 time at expire"
199
+ // creator: "User full yrn"
200
+ // base: "32byte hex string"
201
+ // user: "user name"
202
+ // ip: always null
203
+ // hostname: always null
204
+ // port: always 0
205
+ // cuk: always null
206
+ // extra: always null
207
+ // tenant: if scoped token, this is "tenant name". if not, this is null
208
+ // }
209
+ //
210
+ const rawCreateUserTokenByK8sUser = (user, user_id, tenant, expire_limit) => {
211
+ const resobj = { result: true, message: null };
212
+ if (!k2hr3apiutil_1.default.isSafeString(user)) { // allow another parameter is null
213
+ resobj.result = false;
214
+ resobj.message = 'parameter is wrong : user=' + JSON.stringify(user);
215
+ dbglogging_1.default.elog(resobj.message);
216
+ return resobj;
217
+ }
218
+ const _user = user.toLowerCase();
219
+ if (!k2hr3apiutil_1.default.isString(user_id) || !k2hr3apiutil_1.default.isSafeStrUuid4(user_id)) { // user_id is uuid4
220
+ resobj.result = false;
221
+ resobj.message = 'parameter is wrong : user_id(must be uuid4)=' + JSON.stringify(user_id);
222
+ dbglogging_1.default.elog(resobj.message);
223
+ return resobj;
224
+ }
225
+ const _user_id = user_id;
226
+ let _tenant = null;
227
+ if (k2hr3apiutil_1.default.isSafeString(tenant)) {
228
+ _tenant = tenant;
229
+ }
230
+ let _expire_limit = 24 * 60 * 60; // default 24H
231
+ if (0 < unscopedtoken_exp) {
232
+ _expire_limit = unscopedtoken_exp; // override expire limit by config
233
+ }
234
+ else {
235
+ if (k2hr3apiutil_1.default.isSafeNumber(expire_limit) && 0 < expire_limit) {
236
+ _expire_limit = expire_limit;
237
+ }
238
+ }
239
+ const dkcobj = k2hr3dkc_1.default.getK2hdkc(true, false); // use permanent object(need to clean)
240
+ const keys = (0, k2hr3keys_1.getK2hr3Keys)(_user, null);
241
+ if (!k2hr3apiutil_1.default.isSafeEntity(dkcobj)) {
242
+ resobj.result = false;
243
+ resobj.message = 'Not initialize yet.';
244
+ dbglogging_1.default.elog(resobj.message);
245
+ return resobj;
246
+ }
247
+ const user_id_uuid4 = _user_id; // user id must be UUID4
248
+ const user_ex_id = k2hr3apiutil_1.default.getStrUuid4(); // set seed(uuid4)
249
+ // make token seed value
250
+ const now_unixtime = k2hr3apiutil_1.default.getUnixtime();
251
+ // user token and yrn key
252
+ let user_token = '';
253
+ let user_token_base = '';
254
+ // create key
255
+ for (let is_loop = true; is_loop;) {
256
+ // make user token
257
+ const token_elements = k2hr3apiutil_1.default.makeStringToken256(user_ex_id, user_id_uuid4);
258
+ if (!k2hr3apiutil_1.default.isSafeEntity(token_elements)) {
259
+ resobj.result = false;
260
+ resobj.message = 'could not make token from ' + JSON.stringify(user_ex_id) + ' and ' + JSON.stringify(_user_id);
261
+ dbglogging_1.default.elog(resobj.message);
262
+ dkcobj.clean();
263
+ return resobj;
264
+ }
265
+ user_token = token_elements.str_token;
266
+ user_token_base = token_elements.str_base; // token base
267
+ // user token key
268
+ const token_user_key = keys.TOKEN_USER_TOP_KEY + '/' + user_token; // "yrn:yahoo::::token:user/<user token>"
269
+ // get user token for existing check
270
+ const value = dkcobj.getValue(token_user_key, null, true, null);
271
+ if (!k2hr3apiutil_1.default.isSafeEntity(value)) {
272
+ // succeed uniq token
273
+ break;
274
+ }
275
+ dbglogging_1.default.dlog('conflict user token(' + user_token + ') which already is used, so remake token for uniq.');
276
+ }
277
+ const token_seed = {
278
+ publisher: K8S_PUBLISHER_NAME, // "K8SOIDC"
279
+ userexid: user_ex_id, // seed(uuid4)
280
+ date: (new Date(now_unixtime * 1000)).toISOString(), // now date(UTC ISO 8601)
281
+ expire: (new Date((now_unixtime + _expire_limit) * 1000)).toISOString(), // expire date(UTC ISO 8601)
282
+ creator: keys.USER_KEY, // "yrn:yahoo::::user:<user>"
283
+ base: user_token_base, // token bease
284
+ user: _user, // user(creator)
285
+ ip: null, // ip(creator)
286
+ hostname: null, // hostname(creator)
287
+ port: 0, // port(creator)
288
+ cuk: null, // cuk(creator)
289
+ extra: null, // extra(creator)
290
+ tenant: _tenant // tenant(if scope, not null)
291
+ };
292
+ // Add user token/expire/seed into result object.
293
+ resobj.token = user_token;
294
+ resobj.expire_at = token_seed.expire;
295
+ resobj.token_seed = JSON.stringify(token_seed);
296
+ resobj.userid = _user_id;
297
+ dkcobj.clean();
298
+ return resobj;
299
+ };
300
+ //---------------------------------------------------------
301
+ // Verify User Token Publisher For k8s oidc
302
+ //---------------------------------------------------------
303
+ //
304
+ // token_seed : token seed data
305
+ //
306
+ // result : {
307
+ // result: true/false
308
+ // message: null or error message string
309
+ // }
310
+ //
311
+ const rawWrapVerifyUserTokenPublisherForK8s = (token_seed) => {
312
+ const resobj = { result: true, message: null };
313
+ // parse seed
314
+ if (!k2hr3apiutil_1.default.checkSimpleJSON(token_seed)) {
315
+ resobj.result = false;
316
+ resobj.message = 'token_seed(not printable) is not safe entity.';
317
+ dbglogging_1.default.elog(resobj.message);
318
+ return resobj;
319
+ }
320
+ const tmpseed = k2hr3apiutil_1.default.parseJSON(token_seed);
321
+ if (!k2hr3apiutil_1.default.isValTypeTokenSeed(tmpseed)) {
322
+ resobj.result = false;
323
+ resobj.message = 'token_seed(not printable) is not safe entity.';
324
+ dbglogging_1.default.elog(resobj.message);
325
+ return resobj;
326
+ }
327
+ const seed = tmpseed;
328
+ if (!k2hr3apiutil_1.default.isSafeString(seed.publisher) ||
329
+ (seed.publisher != K8S_PUBLISHER_NAME)) // publisher must be 'K8SOIDC'
330
+ {
331
+ resobj.result = false;
332
+ resobj.message = 'token_seed(not printable) is not safe entity.';
333
+ dbglogging_1.default.elog(resobj.message);
334
+ return resobj;
335
+ }
336
+ return resobj;
337
+ };
338
+ //---------------------------------------------------------
339
+ // Verify User Token (OIDC Token)
340
+ //---------------------------------------------------------
341
+ //
342
+ // dkcobj_permanent : dkcobj object
343
+ // user : target user name for token
344
+ // tenant : target tenant name for token(if token is scoped)
345
+ // token : check token
346
+ // token_seed : token seed data
347
+ //
348
+ // result : {
349
+ // result: true/false
350
+ // message: null or error message string
351
+ // }
352
+ //
353
+ const rawVerifyUserTokenByK8sUser = (dkcobj_permanent, user, tenant, token, token_seed) => {
354
+ const resobj = { result: true, message: null };
355
+ if (!k2hr3apiutil_1.default.isSafeString(token) || !k2hr3apiutil_1.default.isSafeString(token_seed) || !k2hr3apiutil_1.default.isSafeString(user)) {
356
+ resobj.result = false;
357
+ resobj.message = 'some parameters are wrong : token=' + JSON.stringify(token) + ', token_seed=<not printable>, user=' + JSON.stringify(user);
358
+ dbglogging_1.default.elog(resobj.message);
359
+ return resobj;
360
+ }
361
+ // parse seed
362
+ if (!k2hr3apiutil_1.default.checkSimpleJSON(token_seed)) {
363
+ resobj.result = false;
364
+ resobj.message = 'token_seed(not printable) is not safe entity.';
365
+ dbglogging_1.default.elog(resobj.message);
366
+ return resobj;
367
+ }
368
+ const tmpseed = k2hr3apiutil_1.default.parseJSON(token_seed);
369
+ if (!k2hr3apiutil_1.default.isValTypeTokenSeed(tmpseed)) {
370
+ resobj.result = false;
371
+ resobj.message = 'token_seed(not printable) is not safe entity.';
372
+ dbglogging_1.default.elog(resobj.message);
373
+ return resobj;
374
+ }
375
+ // check all seed values
376
+ const seed = tmpseed;
377
+ if (!k2hr3apiutil_1.default.isSafeString(seed.publisher) ||
378
+ (seed.publisher != K8S_PUBLISHER_NAME) || // publisher must be 'K8SOIDC'
379
+ !k2hr3apiutil_1.default.isSafeString(seed.userexid) ||
380
+ !k2hr3apiutil_1.default.isSafeString(seed.date) ||
381
+ !k2hr3apiutil_1.default.isSafeString(seed.expire) ||
382
+ !k2hr3apiutil_1.default.isSafeString(seed.creator) ||
383
+ !k2hr3apiutil_1.default.isSafeString(seed.base) ||
384
+ !k2hr3apiutil_1.default.isSafeString(seed.user) ||
385
+ !k2hr3apiutil_1.default.compareCaseString(seed.user, user)) {
386
+ resobj.result = false;
387
+ resobj.message = 'token_seed(not printable) is not safe entity.';
388
+ dbglogging_1.default.elog(resobj.message);
389
+ return resobj;
390
+ }
391
+ // check expire
392
+ if (k2hr3apiutil_1.default.isExpired(seed.expire)) {
393
+ resobj.result = false;
394
+ resobj.message = 'token is expired by expire date(' + JSON.stringify(seed.expire) + ') in token_seed.';
395
+ dbglogging_1.default.elog(resobj.message);
396
+ return resobj;
397
+ }
398
+ // check tenant name(if tenant is specified, seed must have same tenant name)
399
+ if (k2hr3apiutil_1.default.isSafeString(seed.tenant) !== k2hr3apiutil_1.default.isSafeString(tenant) || (k2hr3apiutil_1.default.isSafeString(seed.tenant) && !k2hr3apiutil_1.default.compareCaseString(seed.tenant, tenant))) {
400
+ resobj.result = false;
401
+ resobj.message = 'token_seed(not printable) is (un)scoped, but tenant name is (not) specified. Then unmatched.';
402
+ dbglogging_1.default.elog(resobj.message);
403
+ return resobj;
404
+ }
405
+ // k2hdkc
406
+ const keys = (0, k2hr3keys_1.getK2hr3Keys)(seed.user, seed.tenant);
407
+ let dkcobj = dkcobj_permanent;
408
+ if (!k2hr3apiutil_1.default.isSafeEntity(dkcobj)) {
409
+ dkcobj = k2hr3dkc_1.default.getK2hdkc(true, false); // use permanent object(need to clean)
410
+ if (!k2hr3apiutil_1.default.isSafeEntity(dkcobj)) {
411
+ resobj.result = false;
412
+ resobj.message = 'Not initialize yet.';
413
+ dbglogging_1.default.elog(resobj.message);
414
+ return resobj;
415
+ }
416
+ }
417
+ // get user id
418
+ const userid = dkcobj.getValue(keys.USER_ID_KEY, null, true, null); // get user id from "yrn:yahoo::::user:<user>:id"
419
+ if (!k2hr3apiutil_1.default.isSafeString(userid)) {
420
+ resobj.result = false;
421
+ resobj.message = 'could not get user id for user(' + seed.user + ').';
422
+ dbglogging_1.default.elog(resobj.message);
423
+ return resobj;
424
+ }
425
+ // make verify token
426
+ const token_elements = k2hr3apiutil_1.default.makeStringToken256(seed.userexid, userid, seed.base);
427
+ if (!k2hr3apiutil_1.default.isSafeEntity(token_elements)) {
428
+ resobj.result = false;
429
+ resobj.message = 'could not make verify token from ' + JSON.stringify(seed.userexid) + ' and ' + JSON.stringify(userid) + ' and ' + JSON.stringify(seed.base);
430
+ dbglogging_1.default.elog(resobj.message);
431
+ return resobj;
432
+ }
433
+ if (token !== token_elements.str_token) {
434
+ resobj.result = false;
435
+ resobj.message = 'token(' + token + ') verify is failure, verify token is ' + token_elements.str_token + '.';
436
+ dbglogging_1.default.elog(resobj.message);
437
+ return resobj;
438
+ }
439
+ return resobj;
440
+ };
441
+ //---------------------------------------------------------
442
+ // Get User/Tenant information by User Token
443
+ //---------------------------------------------------------
444
+ //
445
+ // Result: {
446
+ // result: true/false
447
+ // message: null or error message string
448
+ // user: user name
449
+ // userid: user id
450
+ // tenant: if token is scoped token, this value is set tenant name.
451
+ // }
452
+ //
453
+ const rawGetUserTenantInfoByUserToken = (token) => {
454
+ const resobj = { result: true, message: null };
455
+ if (!k2hr3apiutil_1.default.isSafeString(token)) {
456
+ resobj.result = false;
457
+ resobj.message = 'parameter is wrong : token=' + JSON.stringify(token);
458
+ dbglogging_1.default.elog(resobj.message);
459
+ return resobj;
460
+ }
461
+ const dkcobj = k2hr3dkc_1.default.getK2hdkc(true, false); // use permanent object(need to clean)
462
+ let keys = (0, k2hr3keys_1.getK2hr3Keys)();
463
+ if (!k2hr3apiutil_1.default.isSafeEntity(dkcobj)) {
464
+ resobj.result = false;
465
+ resobj.message = 'Not initialize yet.';
466
+ dbglogging_1.default.elog(resobj.message);
467
+ return resobj;
468
+ }
469
+ // get token key under user key
470
+ const token_value_key = keys.TOKEN_USER_TOP_KEY + '/' + token; // "yrn:yahoo::::token:user/<token>"
471
+ const user_token_key = dkcobj.getValue(token_value_key, null, true, null); // "yrn:yahoo::::user:<user>:tenant/{<tenant>}/token/<token>"
472
+ if (!k2hr3apiutil_1.default.isSafeString(user_token_key)) {
473
+ resobj.result = false;
474
+ resobj.message = 'token key(' + token_value_key + ') for token(' + token + ') is not existed.';
475
+ dbglogging_1.default.elog(resobj.message);
476
+ dkcobj.clean();
477
+ return resobj;
478
+ }
479
+ // get user name and tenant name from token key yrn path
480
+ const pattern = new RegExp('^' + keys.MATCH_ANY_USER_TOKEN); // regex = /^yrn:yahoo::::user:(.*):tenant\/(.*)\/token\/(.*)/
481
+ const matches = user_token_key.match(pattern); // reverse to user/tenant names
482
+ if (!k2hr3apiutil_1.default.isNotEmptyArray(matches) || matches.length < 4 || '' === k2hr3apiutil_1.default.getSafeString(matches[1])) {
483
+ resobj.result = false;
484
+ resobj.message = 'token key(' + token_value_key + ') for token(' + token + ') has wrong format value(' + user_token_key + ')';
485
+ dbglogging_1.default.elog(resobj.message);
486
+ dkcobj.clean();
487
+ return resobj;
488
+ }
489
+ const token_user = k2hr3apiutil_1.default.getSafeString(matches[1]);
490
+ const token_tenant = k2hr3apiutil_1.default.isString(matches[2]) ? matches[2] : null;
491
+ // get token seed
492
+ const user_token_seed_key = user_token_key + '/' + keys.SEED_KW; // "yrn:yahoo::::user:<user>:tenant/{<tenant>}/token/<token>/seed"
493
+ const token_seed = dkcobj.getValue(user_token_seed_key, null, true, null);
494
+ if (!k2hr3apiutil_1.default.isSafeString(token_seed)) {
495
+ resobj.result = false;
496
+ resobj.message = 'token key(' + token_value_key + ') for token(' + token + ') does not have token seed data.';
497
+ dbglogging_1.default.elog(resobj.message);
498
+ dkcobj.clean();
499
+ return resobj;
500
+ }
501
+ // verify token
502
+ const vres = rawVerifyUserTokenByK8sUser(dkcobj, token_user, token_tenant, token, token_seed);
503
+ if (!vres.result) {
504
+ resobj.result = false;
505
+ resobj.message = 'failed to verify token(' + token + ') with seed by ' + vres.message;
506
+ dbglogging_1.default.elog(resobj.message);
507
+ dkcobj.clean();
508
+ return resobj;
509
+ }
510
+ // get user id
511
+ keys = (0, k2hr3keys_1.getK2hr3Keys)(token_user, null); // remake keys
512
+ const userid = dkcobj.getValue(keys.USER_ID_KEY, null, true, null); // get user id from "yrn:yahoo::::user:<user>:id"
513
+ if (!k2hr3apiutil_1.default.isSafeString(userid)) {
514
+ resobj.result = false;
515
+ resobj.message = 'could not get user id for user(' + token_user + ').';
516
+ dbglogging_1.default.elog(resobj.message);
517
+ dkcobj.clean();
518
+ return resobj;
519
+ }
520
+ dkcobj.clean();
521
+ // make result
522
+ resobj.user = token_user;
523
+ resobj.userid = userid;
524
+ resobj.tenant = token_tenant;
525
+ return resobj;
526
+ };
527
+ //---------------------------------------------------------
528
+ // Verify (Un)scoped Token
529
+ //---------------------------------------------------------
530
+ //
531
+ // token : unscoped/scoped token
532
+ //
533
+ const rawVerifyUnscopedToken = (token) => {
534
+ if (!k2hr3apiutil_1.default.isSafeString(token)) {
535
+ dbglogging_1.default.elog('token(' + JSON.stringify(token) + ') parameter is wrong.');
536
+ return false;
537
+ }
538
+ const dkcobj = k2hr3dkc_1.default.getK2hdkc(true, false); // use permanent object(need to clean)
539
+ const keys = (0, k2hr3keys_1.getK2hr3Keys)();
540
+ if (!k2hr3apiutil_1.default.isSafeEntity(dkcobj)) {
541
+ dbglogging_1.default.elog('K2hdkc client is not initialized yet.');
542
+ return false;
543
+ }
544
+ // get token key under user key
545
+ const token_value_key = keys.TOKEN_USER_TOP_KEY + '/' + token; // "yrn:yahoo::::token:user/<token>"
546
+ const user_token_key = dkcobj.getValue(token_value_key, null, true, null); // "yrn:yahoo::::user:<user>:tenant/{<tenant>}/token/<token>"
547
+ if (!k2hr3apiutil_1.default.isSafeString(user_token_key)) {
548
+ dbglogging_1.default.elog('token key(' + token_value_key + ') for token(' + token + ') is not existed.');
549
+ dkcobj.clean();
550
+ return false;
551
+ }
552
+ // get user name and tenant name from token key yrn path
553
+ const pattern = new RegExp('^' + keys.MATCH_ANY_USER_TOKEN); // regex = /^yrn:yahoo::::user:(.*):tenant\/(.*)\/token\/(.*)/
554
+ const matches = user_token_key.match(pattern); // reverse to user/tenant names
555
+ if (!k2hr3apiutil_1.default.isNotEmptyArray(matches) || matches.length < 4 || '' === k2hr3apiutil_1.default.getSafeString(matches[1])) {
556
+ dbglogging_1.default.elog('token key(' + token_value_key + ') for token(' + token + ') has wrong format value(' + user_token_key + ')');
557
+ dkcobj.clean();
558
+ return false;
559
+ }
560
+ const token_user = k2hr3apiutil_1.default.getSafeString(matches[1]);
561
+ let token_tenant = k2hr3apiutil_1.default.getSafeString(matches[2]);
562
+ if ('' === token_tenant) {
563
+ token_tenant = null;
564
+ }
565
+ // get token seed
566
+ const user_token_seed_key = user_token_key + '/' + keys.SEED_KW; // "yrn:yahoo::::user:<user>:tenant/{<tenant>}/token/<token>/seed"
567
+ const token_seed = dkcobj.getValue(user_token_seed_key, null, true, null);
568
+ if (!k2hr3apiutil_1.default.isSafeString(token_seed)) {
569
+ dbglogging_1.default.elog('token key(' + token_value_key + ') for token(' + token + ') does not have token seed data.');
570
+ dkcobj.clean();
571
+ return false;
572
+ }
573
+ // verify token
574
+ const vres = rawVerifyUserTokenByK8sUser(dkcobj, token_user, token_tenant, token, token_seed);
575
+ if (!vres.result) {
576
+ dbglogging_1.default.elog('failed to verify token(' + token + ') with seed by ' + vres.message);
577
+ dkcobj.clean();
578
+ return false;
579
+ }
580
+ dkcobj.clean();
581
+ return true;
582
+ };
583
+ //---------------------------------------------------------
584
+ // Get Scoped token from k8s user token
585
+ //---------------------------------------------------------
586
+ //
587
+ // callback(error, result):
588
+ // result = {
589
+ // user: user name
590
+ // userid: user id
591
+ // scoped: always true
592
+ // token: token string
593
+ // expire: expire string(UTC ISO 8601)
594
+ // region: region string
595
+ // token_seed: JSON token seed data
596
+ // }
597
+ //
598
+ // [NOTE]
599
+ // The token is allowed scoped token, but it must be same tenant token.
600
+ //
601
+ const rawGetUserScopedTokenK8s = (token, tenant, callback) => {
602
+ if (!k2hr3apiutil_1.default.isSafeString(token) || !k2hr3apiutil_1.default.isSafeString(tenant)) {
603
+ const error = new Error('some parameters are wrong : token=' + JSON.stringify(token) + ', tenant=' + JSON.stringify(tenant));
604
+ dbglogging_1.default.elog(error.message);
605
+ callback(error, null);
606
+ return;
607
+ }
608
+ // verify and get user/tenant information
609
+ const token_info = rawGetUserTenantInfoByUserToken(token);
610
+ if (!token_info.result) {
611
+ const error = new Error('could not get any information from token(' + token + '), result : ' + token_info.message);
612
+ dbglogging_1.default.elog(error.message);
613
+ callback(error, null);
614
+ return;
615
+ }
616
+ // check tenant name
617
+ if (k2hr3apiutil_1.default.isSafeString(token_info.tenant) && token_info.tenant !== tenant) {
618
+ const error = new Error('token(' + token + ') has scoped(' + token_info.tenant + '), but it is not as same as the request tenant(' + tenant + ').');
619
+ dbglogging_1.default.elog(error.message);
620
+ callback(error, null);
621
+ return;
622
+ }
623
+ // create scoped token
624
+ const resobj = rawCreateUserTokenByK8sUser(k2hr3apiutil_1.default.getSafeString(token_info.user), k2hr3apiutil_1.default.getSafeString(token_info.userid), tenant); // not specify expire limit now(using default).
625
+ if (!resobj.result) {
626
+ const error = new Error('could not create user scoped token for uname(' + token_info.user + ')/user id(' + token_info.userid + ') for tenant(' + tenant + ').');
627
+ dbglogging_1.default.elog(error.message);
628
+ callback(error, null);
629
+ return;
630
+ }
631
+ // make result
632
+ const result = {
633
+ user: k2hr3apiutil_1.default.getSafeString(token_info.user),
634
+ userid: k2hr3apiutil_1.default.getSafeString(token_info.userid),
635
+ scoped: true,
636
+ token: k2hr3apiutil_1.default.getSafeString(resobj.token),
637
+ expire: k2hr3apiutil_1.default.isString(resobj.expire_at) ? resobj.expire_at : null,
638
+ region: K8S_REGION_NAME,
639
+ token_seed: k2hr3apiutil_1.default.getSafeString(resobj.token_seed),
640
+ };
641
+ callback(null, result);
642
+ };
643
+ //---------------------------------------------------------
644
+ // Get Unscoped token by oidc token
645
+ //---------------------------------------------------------
646
+ //
647
+ // callback(error, result):
648
+ // result = {
649
+ // user: user name User name in token: set user name if specified user name key name in config. if not specified, set user id
650
+ // userid: user id User id in token: payload in token has 'sub' key, it is user id.
651
+ // scoped: false (always false)
652
+ // token: token string(id) OIDC Token
653
+ // expire: expire string expire in token: payload in token has 'exp' key, it is expire unix time.
654
+ // region: region string (always n/a)
655
+ // token_seed: seed ({publisher: 'K8SOIDC'})
656
+ // }
657
+ //
658
+ //
659
+ // Utility - Verify OIDC token and get user name
660
+ //
661
+ // token: oidc token
662
+ //
663
+ const rawVerifyTokenAndGetUsername = async (token) => {
664
+ const jwtParam = {
665
+ issuer: oidc_issuer ?? undefined,
666
+ audience: oidc_audience ?? undefined
667
+ };
668
+ const myPromise = (issuer_url, conf_key) => {
669
+ return new Promise((resolve, reject) => {
670
+ https.get(issuer_url + '/.well-known/openid-configuration', (res) => {
671
+ if (res.statusCode !== 200) {
672
+ res.resume();
673
+ reject('statusCode should be 200, not ' + String(res.statusCode));
674
+ }
675
+ res.setEncoding('utf8');
676
+ let rawData = '';
677
+ res.on('data', (chunk) => {
678
+ rawData += chunk;
679
+ });
680
+ res.on('end', () => {
681
+ const parsedData = k2hr3apiutil_1.default.parseJSON(rawData);
682
+ if (k2hr3apiutil_1.default.isPlainObject(parsedData) && k2hr3apiutil_1.default.isString(parsedData[conf_key])) {
683
+ resolve(parsedData[conf_key]);
684
+ }
685
+ else {
686
+ const errorMsg = ('the ' + conf_key + ' key should exist, but no such a key');
687
+ dbglogging_1.default.elog(errorMsg);
688
+ reject(errorMsg);
689
+ }
690
+ });
691
+ }).on('error', (err) => {
692
+ reject(err);
693
+ });
694
+ });
695
+ };
696
+ const asyncFunction = async () => {
697
+ let oidc_jwks_uri;
698
+ try {
699
+ oidc_jwks_uri = await myPromise(k2hr3apiutil_1.default.getSafeString(oidc_issuer), OIDC_JWKS_URI_KEYNAME);
700
+ if (!k2hr3apiutil_1.default.isSafeString(oidc_jwks_uri)) {
701
+ const error = new Error('oidc_jwks_uri should be defined, but no oidc_jwks_uri.');
702
+ dbglogging_1.default.elog(error.message);
703
+ throw error;
704
+ }
705
+ }
706
+ catch (err) {
707
+ if (err instanceof Error) {
708
+ dbglogging_1.default.elog(err.message);
709
+ }
710
+ throw err;
711
+ }
712
+ const JWKS = (0, jose_1.createRemoteJWKSet)(new URL(oidc_jwks_uri));
713
+ let payload;
714
+ try {
715
+ const jwtResult = await (0, jose_1.jwtVerify)(token, JWKS, jwtParam);
716
+ payload = jwtResult.payload;
717
+ }
718
+ catch (err) {
719
+ if (err instanceof Error) {
720
+ dbglogging_1.default.elog(err.message);
721
+ }
722
+ throw err;
723
+ }
724
+ let userName = null;
725
+ if (k2hr3apiutil_1.default.isPlainObject(payload) && k2hr3apiutil_1.default.isSafeString(oidc_username) && k2hr3apiutil_1.default.isString(payload[oidc_username])) {
726
+ userName = k2hr3apiutil_1.default.getSafeString(payload[oidc_username]);
727
+ }
728
+ else {
729
+ if (k2hr3apiutil_1.default.isPlainObject(payload) && k2hr3apiutil_1.default.isSafeString(payload.sub)) {
730
+ userName = payload.sub;
731
+ }
732
+ }
733
+ if (!k2hr3apiutil_1.default.isSafeString(userName)) {
734
+ const error = new Error('failed to verify token for getting user name.');
735
+ dbglogging_1.default.elog(error.message);
736
+ throw error;
737
+ }
738
+ return userName;
739
+ };
740
+ return asyncFunction();
741
+ };
742
+ const rawGetUserUnscopedTokenK8s = (token, callback) => {
743
+ if (!k2hr3apiutil_1.default.isSafeString(token)) {
744
+ const error = new Error('oidc token parameter is not string or empty.');
745
+ dbglogging_1.default.elog(error.message);
746
+ callback(error, null);
747
+ return;
748
+ }
749
+ //
750
+ // Check the id_token.
751
+ //
752
+ // see. https://openid.net/specs/openid-connect-core-1_0.html#IDToken
753
+ //
754
+ const parts = token.split('.', 2);
755
+ if (2 !== parts.length) {
756
+ const error = new Error('oidc token must have two parts, but it has ' + parts.length + ' parts.');
757
+ dbglogging_1.default.elog(error.message);
758
+ callback(error, null);
759
+ return;
760
+ }
761
+ //
762
+ // decode part[1] to payload
763
+ //
764
+ const payload = k2hr3apiutil_1.default.parseJSON(new TextDecoder().decode(decode(parts[1])));
765
+ if (!k2hr3apiutil_1.default.isPlainObject(payload)) {
766
+ const error = new Error('could not decode json from the part[1] in oidc token.');
767
+ dbglogging_1.default.elog(error.message);
768
+ callback(error, null);
769
+ return;
770
+ }
771
+ //
772
+ // payload must have 'sub' key for user id
773
+ //
774
+ let userid;
775
+ if (k2hr3apiutil_1.default.isSafeString(payload.sub)) {
776
+ userid = payload.sub;
777
+ }
778
+ else {
779
+ const error = new Error('token payload should contain sub(userid), but not find sub(userid).');
780
+ dbglogging_1.default.elog(error.message);
781
+ callback(error, null);
782
+ return;
783
+ }
784
+ //
785
+ // get user name from payload
786
+ //
787
+ let username;
788
+ if (k2hr3apiutil_1.default.isSafeString(oidc_username)) {
789
+ if (k2hr3apiutil_1.default.isSafeString(payload[oidc_username])) {
790
+ username = k2hr3apiutil_1.default.getSafeString(payload[oidc_username]);
791
+ }
792
+ else {
793
+ const error = new Error('token payload should contain user name(' + oidc_username + '), but not find it.');
794
+ dbglogging_1.default.elog(error.message);
795
+ callback(error, null);
796
+ return;
797
+ }
798
+ }
799
+ else {
800
+ // If user name key is not specified, user id will be used instead.
801
+ username = userid;
802
+ }
803
+ //
804
+ // verify token by JWT library and get user name
805
+ //
806
+ const _callback = callback;
807
+ const lower_username = username.toLowerCase(); // to lower case
808
+ rawVerifyTokenAndGetUsername(token).then((result) => {
809
+ const verified_username = k2hr3apiutil_1.default.getSafeString(result);
810
+ //
811
+ // compare user name
812
+ //
813
+ if (!k2hr3apiutil_1.default.compareCaseString(lower_username, verified_username)) {
814
+ const error = new Error('oidc token has ' + lower_username + ' username, but verified username(' + verified_username + ') is different.');
815
+ dbglogging_1.default.elog(error.message);
816
+ _callback(error, null);
817
+ return;
818
+ }
819
+ // core seed
820
+ const user_id_uuid4 = k2hr3apiutil_1.default.cvtNumberStringToUuid4(userid, 10); // payload.sub is decimal string
821
+ let expire_limit;
822
+ if (k2hr3apiutil_1.default.isSafeNumber(payload['exp'])) {
823
+ expire_limit = payload['exp'] - k2hr3apiutil_1.default.getUnixtime();
824
+ if (expire_limit <= 0) {
825
+ expire_limit = 24 * 60 * 60; // default 24H
826
+ }
827
+ }
828
+ else {
829
+ expire_limit = 24 * 60 * 60; // default 24H
830
+ }
831
+ // create token
832
+ const resobj = rawCreateUserTokenByK8sUser(lower_username, user_id_uuid4, null, expire_limit);
833
+ if (!resobj.result) {
834
+ const error = new Error('could not create user token for uname(' + lower_username + ') or something wrong result : ' + resobj.message);
835
+ dbglogging_1.default.elog(error.message);
836
+ _callback(error, null);
837
+ return;
838
+ }
839
+ // make result
840
+ const resultobj = {
841
+ user: lower_username,
842
+ userid: k2hr3apiutil_1.default.getSafeString(resobj.userid),
843
+ scoped: false,
844
+ token: k2hr3apiutil_1.default.getSafeString(resobj.token),
845
+ expire: k2hr3apiutil_1.default.isSafeString(resobj.expire_at) ? resobj.expire_at : null,
846
+ region: K8S_REGION_NAME,
847
+ token_seed: k2hr3apiutil_1.default.getSafeString(resobj.token_seed)
848
+ };
849
+ _callback(null, resultobj);
850
+ }).catch((err) => {
851
+ dbglogging_1.default.elog(err.message);
852
+ _callback(err, null);
853
+ });
854
+ };
855
+ const hasSetDefaultAuthentication = (obj) => {
856
+ return k2hr3apiutil_1.default.isPlainObject(obj) && k2hr3apiutil_1.default.isFunction(obj.setDefaultAuthentication);
857
+ };
858
+ //
859
+ // unscopedtoken: oidc token(Not use)
860
+ //
861
+ const rawGetUserTenantListK8s = (unscopedtoken, callback) => {
862
+ if (!k2hr3apiutil_1.default.isSafeString(unscopedtoken)) {
863
+ const error = new Error('unscopedtoken parameter is wrong.');
864
+ dbglogging_1.default.elog(error.message);
865
+ callback(error, null);
866
+ return;
867
+ }
868
+ // Verify unscoped token
869
+ if (!rawVerifyUnscopedToken(unscopedtoken)) {
870
+ const error = new Error('unscopedtoken is not safe, varidation is failed.');
871
+ dbglogging_1.default.elog(error.message);
872
+ callback(error, null);
873
+ return;
874
+ }
875
+ const _callback = callback;
876
+ //
877
+ // Get Namespaces by Service Access token
878
+ //
879
+ const cluster = {
880
+ name: 'k2hr3-api-k8soidc-cluster',
881
+ server: k2hr3apiutil_1.default.getSafeString(k8s_api_url),
882
+ caFile: k2hr3apiutil_1.default.getSafeString(k8s_ca_cert),
883
+ skipTLSVerify: false
884
+ };
885
+ const user = {
886
+ name: 'k2hr3-api-k8soidc-name',
887
+ token: k2hr3apiutil_1.default.getSafeString(k2hr3_k8s_sa_token) // [NOTE] k2hr3_k8s_sa_token is global variable
888
+ };
889
+ const kubeconfig = new client_node_1.KubeConfig();
890
+ kubeconfig.loadFromClusterAndUser(cluster, user);
891
+ const k8sApi = kubeconfig.makeApiClient(client_node_1.CoreV1Api);
892
+ if (hasSetDefaultAuthentication(k8sApi)) {
893
+ k8sApi.setDefaultAuthentication({
894
+ applyToRequest: (opts) => {
895
+ if (!k2hr3apiutil_1.default.isPlainObject(opts.headers)) {
896
+ opts.headers = {};
897
+ }
898
+ opts.headers.Authorization = 'Bearer ' + k2hr3_k8s_sa_token; // [NOTE] k2hr3_k8s_sa_token is global variable
899
+ opts.rejectUnauthorized = false;
900
+ }
901
+ });
902
+ }
903
+ else {
904
+ const error = new Error('Not found CoreV1Api.setDefaultAuthentication function.');
905
+ dbglogging_1.default.elog(error.message);
906
+ callback(error, null);
907
+ return;
908
+ }
909
+ const k8s_ns = ['kube-node-lease', 'kube-public', 'kube-system', 'kubernetes-dashboard'];
910
+ const resarr = [];
911
+ k8sApi.listNamespace().then((response) => {
912
+ if (k2hr3apiutil_1.default.isPlainObject(response) && k2hr3apiutil_1.default.isPlainObject(response.body) && k2hr3apiutil_1.default.isArray(response.body.items)) {
913
+ for (let pos = 0; pos < response.body.items.length; ++pos) {
914
+ // check body...items
915
+ const tmpItem = response.body.items[pos];
916
+ if (!k2hr3apiutil_1.default.isPlainObject(tmpItem) ||
917
+ !k2hr3apiutil_1.default.isPlainObject(tmpItem.metadata) ||
918
+ !k2hr3apiutil_1.default.isSafeString(tmpItem.metadata.name)) {
919
+ dbglogging_1.default.wlog('one of response for project(tenant) list is something wrong : ' + JSON.stringify(response.body));
920
+ continue;
921
+ }
922
+ // Is the k8s cluster namespace in kubernetes system namespaces?
923
+ if (k2hr3apiutil_1.default.findStringInArray(k8s_ns, tmpItem.metadata.name)) {
924
+ continue;
925
+ }
926
+ const tenant = {
927
+ name: tmpItem.metadata.name,
928
+ id: k2hr3apiutil_1.default.getSafeString(tmpItem.metadata.uid),
929
+ description: tmpItem.metadata.name,
930
+ display: tmpItem.metadata.name
931
+ };
932
+ resarr.push(tenant);
933
+ }
934
+ }
935
+ if (0 === resarr.length) {
936
+ const error = new Error('no tenant exists');
937
+ dbglogging_1.default.elog(error.message);
938
+ _callback(error, null);
939
+ return;
940
+ }
941
+ _callback(null, resarr);
942
+ }).catch((error) => {
943
+ dbglogging_1.default.elog(error.message);
944
+ _callback(error, null);
945
+ });
946
+ };
947
+ //---------------------------------------------------------
948
+ // Exports
949
+ //---------------------------------------------------------
950
+ exports.k8soidc = {
951
+ //
952
+ // uname: username
953
+ // passwd: passwd
954
+ //
955
+ getUserUnscopedToken: (uname, passwd, callback) => {
956
+ const error = new Error('getUserUnscopedToken is not implemented');
957
+ dbglogging_1.default.elog(error.message);
958
+ callback(error, null);
959
+ },
960
+ //
961
+ // Get Unscoped Token
962
+ //
963
+ // token: OIDC token
964
+ //
965
+ getUserUnscopedTokenByToken: rawGetUserUnscopedTokenK8s,
966
+ //
967
+ // Get Scoped Token
968
+ //
969
+ // tenantid: not used
970
+ //
971
+ getUserScopedToken: rawGetUserScopedTokenK8s,
972
+ //
973
+ // Verify publisher type in seed
974
+ //
975
+ verifyUserTokenPublisher: rawWrapVerifyUserTokenPublisherForK8s,
976
+ //
977
+ // Verify token
978
+ //
979
+ verifyUserToken: (dkcobj_permanent, user, tenant, token, token_seed) => {
980
+ return rawVerifyUserTokenByK8sUser(dkcobj_permanent, user, tenant, token, token_seed);
981
+ },
982
+ //
983
+ // Get tenant list
984
+ //
985
+ getUserTenantList: (unscopedtoken, userid, callback) => {
986
+ rawGetUserTenantListK8s(unscopedtoken, callback);
987
+ }
988
+ };
989
+ //
990
+ // Default
991
+ //
992
+ exports.default = exports.k8soidc;
993
+ /*
994
+ * Local variables:
995
+ * tab-width: 4
996
+ * c-basic-offset: 4
997
+ * End:
998
+ * vim600: noexpandtab sw=4 ts=4 fdm=marker
999
+ * vim<600: noexpandtab sw=4 ts=4
1000
+ */