k2hr3-api 1.0.23 → 1.0.25

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 (42) hide show
  1. package/ChangeLog +12 -0
  2. package/app.js +63 -30
  3. package/bin/run.sh +14 -0
  4. package/config/default.json +3 -2
  5. package/lib/k2hr3config.js +12 -0
  6. package/lib/k2hr3dkc.js +903 -13
  7. package/lib/k2hr3keys.js +1 -0
  8. package/lib/k2hr3tokens.js +53 -0
  9. package/package.json +11 -6
  10. package/routes/tenant.js +1014 -0
  11. package/tests/auto_all_spec.js +4 -0
  12. package/tests/auto_extdata.js +2 -0
  13. package/tests/auto_tenant.js +989 -0
  14. package/tests/auto_tenant_spec.js +79 -0
  15. package/tests/auto_userdata.js +15 -12
  16. package/tests/manual_acr_delete.js +1 -0
  17. package/tests/manual_acr_get.js +1 -0
  18. package/tests/manual_acr_postput.js +1 -0
  19. package/tests/manual_allusertenant_get.js +58 -3
  20. package/tests/manual_extdata_get.js +1 -0
  21. package/tests/manual_list_gethead.js +1 -0
  22. package/tests/manual_policy_delete.js +1 -0
  23. package/tests/manual_policy_gethead.js +3 -1
  24. package/tests/manual_policy_postput.js +1 -0
  25. package/tests/manual_resource_delete.js +1 -0
  26. package/tests/manual_resource_gethead.js +1 -0
  27. package/tests/manual_resource_postput.js +1 -0
  28. package/tests/manual_role_delete.js +2 -0
  29. package/tests/manual_role_gethead.js +4 -0
  30. package/tests/manual_role_postput.js +2 -0
  31. package/tests/manual_service_delete.js +1 -0
  32. package/tests/manual_service_gethead.js +1 -0
  33. package/tests/manual_service_postput.js +1 -0
  34. package/tests/manual_tenant_delete.js +152 -0
  35. package/tests/manual_tenant_gethead.js +268 -0
  36. package/tests/manual_tenant_postput.js +293 -0
  37. package/tests/manual_test.sh +21 -7
  38. package/tests/manual_userdata_get.js +1 -0
  39. package/tests/manual_usertoken_gethead.js +1 -0
  40. package/tests/manual_usertoken_postput.js +1 -0
  41. package/tests/manual_version_get.js +1 -0
  42. package/tests/test.sh +2 -0
@@ -0,0 +1,1014 @@
1
+ /*
2
+ * K2HR3 REST API
3
+ *
4
+ * Copyright 2023 Yahoo Japan Corporation.
5
+ *
6
+ * K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
7
+ * common management information for the cloud.
8
+ * K2HR3 can dynamically manage information as "who", "what", "operate".
9
+ * These are stored as roles, resources, policies in K2hdkc, and the
10
+ * client system can dynamically read and modify these information.
11
+ *
12
+ * For the full copyright and license information, please view
13
+ * the license file that was distributed with this source code.
14
+ *
15
+ * AUTHOR: Takeshi Nakatani
16
+ * CREATE: Mon Sep 3 2023
17
+ * REVISION:
18
+ *
19
+ */
20
+
21
+ 'use strict';
22
+
23
+ var express = require('express');
24
+ var router = express.Router();
25
+
26
+ var r3token = require('../lib/k2hr3tokens');
27
+ var apiutil = require('../lib/k2hr3apiutil');
28
+ var resutil = require('../lib/k2hr3resutil');
29
+ var k2hr3 = require('../lib/k2hr3dkc');
30
+ var r3keys = require('../lib/k2hr3keys').getK2hr3Keys;
31
+
32
+ // Debug logging objects
33
+ var r3logger = require('../lib/dbglogging');
34
+
35
+ //=========================================================
36
+ // CAUTION
37
+ //---------------------------------------------------------
38
+ // This note is common to the TENANT API.
39
+ //
40
+ // TENANT API requires User Unscoped Token or User Scoped
41
+ // Token.
42
+ // Note that even if a User Scoped Token is specified, that
43
+ // scoped Tenant will be ignored.
44
+ // To specify the tenant of each API, use URI path or parameter
45
+ // instead of Token.
46
+ // Specify the tenant only by the tenant name, not by the YRN
47
+ // full path.
48
+ //
49
+ //---------------------------------------------------------
50
+
51
+ //=========================================================
52
+ // Common Utility function
53
+ //=========================================================
54
+ //
55
+ // Utility for parsing common input parameters
56
+ //
57
+ // This function parse token(user or role or not have this) from HTTP request(req),
58
+ // and tenant name, etc.
59
+ //
60
+ // return {
61
+ // result: true/false
62
+ // message: null or error message
63
+ // status_code: status code(default 200)
64
+ // parameters: {
65
+ // token_type: null or 'user' or 'role'
66
+ // token_str: token string(if user token or role token)
67
+ // token_info: null or object(returned from checkToken)
68
+ // user_name: null or user name(if user token)
69
+ // tenant_name: null or tenant name in request uri path
70
+ // keys: k2hr3keys object
71
+ // }
72
+ // }
73
+ //
74
+ function rawParseBaseParamInRequest(req)
75
+ {
76
+ var result = {
77
+ result: true,
78
+ message: null,
79
+ status_code: 200
80
+ };
81
+ var parameters = {
82
+ token_type: null,
83
+ token_str: null,
84
+ token_info: null,
85
+ token_tenant: null,
86
+ user_name: null,
87
+ tenant_name: null,
88
+ keys: r3keys() // temporary
89
+ };
90
+
91
+ //
92
+ // check token for API mode
93
+ //
94
+ if(r3token.hasAuthTokenHeader(req)){
95
+ var token_result = r3token.checkToken(req, false, true); // (un)scoped, user
96
+ if(!token_result.result){
97
+ result.result = token_result.result;
98
+ result.message = token_result.message;
99
+ result.status_code = token_result.status;
100
+ r3logger.elog(result.message);
101
+ return result;
102
+ }
103
+ parameters.token_str = token_result.token;
104
+ parameters.token_type = token_result.token_type;
105
+ parameters.token_info = token_result.token_info;
106
+ parameters.user_name = apiutil.getSafeString(parameters.token_info.user);
107
+ }
108
+
109
+ //
110
+ // get tenant name from uri
111
+ //
112
+ var requestptn = new RegExp('^/v1/tenant/(.*)'); // regex = /^\/v1\/tenant\/(.*)/
113
+ var reqmatchs = decodeURI(req.baseUrl).match(requestptn);
114
+ if(apiutil.isEmptyArray(reqmatchs) || reqmatchs.length < 2 || '' === apiutil.getSafeString(reqmatchs[1])){
115
+ parameters.tenant_name = null;
116
+ }else{
117
+ // check tenant name
118
+ if(0 !== reqmatchs[1].indexOf(parameters.keys.VALUE_PREFIX_LOCAL_TENANT)){
119
+ // Not have prefix("local@")
120
+ parameters.tenant_name = parameters.keys.VALUE_PREFIX_LOCAL_TENANT + reqmatchs[1];
121
+ parameters.tenant_name = parameters.tenant_name.toLowerCase();
122
+ }else{
123
+ parameters.tenant_name = reqmatchs[1].toLowerCase();
124
+ }
125
+ }
126
+
127
+ // keys
128
+ parameters.keys = r3keys(parameters.user_name, parameters.tenant_name);
129
+
130
+ // no error
131
+ result.parameters = parameters;
132
+
133
+ return result;
134
+ }
135
+
136
+ //
137
+ // Utility for Create/Update tenant for POST/PUT
138
+ //
139
+ // is_create : true/false
140
+ // user_name : add main user name for tenant
141
+ // tenant_name : tenant name
142
+ // tenant_id : tenant id
143
+ // tenant_desc : tenant description
144
+ // tenant_display : tenant display name
145
+ // tenant_users : other tenant user names
146
+ //
147
+ // result {
148
+ // result: true/false
149
+ // message: error message
150
+ // rescode: 200/201/4xx/5xx
151
+ // }
152
+ //
153
+ function rawCreateUpdateTenant(is_create, user_name, tenant_name, tenant_id, tenant_desc, tenant_display, tenant_users)
154
+ {
155
+ var result = {result: true, message: null, rescode: 200};
156
+ var resobj;
157
+
158
+ if(is_create){
159
+ //
160
+ // Create tenant
161
+ //
162
+ resobj = k2hr3.findTenant(tenant_name);
163
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeEntity(resobj.result) && true === resobj.result){
164
+ result.result = false;
165
+ result.message = 'failed to create tenant by already tenant(' + tenant_name + ') existed';
166
+ result.rescode = 400;
167
+ r3logger.elog(result.message);
168
+ return result;
169
+ }
170
+ result.rescode = 201; // 201: Created
171
+ }else{
172
+ //
173
+ // Update tenant
174
+ //
175
+ resobj = k2hr3.findTenant(tenant_name, user_name, tenant_id);
176
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || false === resobj.result){
177
+ result.result = false;
178
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
179
+ result.message = 'failed to update tenant by ' + resobj.message;
180
+ }else{
181
+ result.message = 'failed to update tenant by unknown reason';
182
+ }
183
+ result.rescode = 400;
184
+ r3logger.elog(result.message);
185
+ return result;
186
+ }
187
+ result.rescode = 200; // 200: OK
188
+ }
189
+
190
+ //
191
+ // Create/Update tenant
192
+ //
193
+ resobj = k2hr3.initTenant(tenant_name, tenant_id, tenant_desc, tenant_display, user_name, tenant_users);
194
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || false === resobj.result){
195
+ result.result = false;
196
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
197
+ result.message = 'failed to create tenant by ' + resobj.message;
198
+ }else{
199
+ result.message = 'failed to create tenant by unknown reason';
200
+ }
201
+ result.rescode = 400;
202
+ r3logger.elog(result.message);
203
+ return result;
204
+ }
205
+
206
+ return result;
207
+ }
208
+
209
+ //=========================================================
210
+ // Router POST
211
+ //=========================================================
212
+ //
213
+ // Mountpath : '/v1/tenant'
214
+ //
215
+ //---------------------------------------------------------
216
+ // [POST] No tenant path
217
+ //---------------------------------------------------------
218
+ // POST '/v1/tenant' : create tenant version 1
219
+ // HEADER : X-Auth-Token = <User token>
220
+ // body : {
221
+ // "tenant": {
222
+ // "name": <tenant> => key is "yrn:yahoo:::<tenant>"
223
+ // thix value type must be string.
224
+ // "desc": <description> => value for "yrn:yahoo:::<tenant>:desc"
225
+ // thix value type must be string.
226
+ // "display": <display name> => key is "yrn:yahoo:::<tenant>:display"
227
+ // thix value type must be string.
228
+ // "users": <user> or [user, ...] => key is "yrn:yahoo::::user:<user>"
229
+ // specify adding user array which is yrn path as "yrn:yahoo::::user:<user>" or "user name"
230
+ // }
231
+ // }
232
+ //
233
+ // response status code : 201 or 4xx/5xx
234
+ // response body : {
235
+ // result: true/false
236
+ // message: messages
237
+ // }
238
+ //
239
+ // Create a tenant as <K2HR3 cluster LOCAL> tenant.
240
+ //
241
+ // [NOTE]
242
+ // If the <K2HR3 cluster LOCAL> tenant already exists, this repsponses an error.
243
+ //
244
+ // Tenant names must start with "local@"(if not set it, this prefix adds automatically).
245
+ // Specify the user by YRN full path or user name.
246
+ // If the user indicated by <User Token> does not exist, it will be added.
247
+ // New tenant id is set automatically.
248
+ //
249
+ //---------------------------------------------------------
250
+ // [POST] With tenant path
251
+ //---------------------------------------------------------
252
+ // POST '/v1/tenant/tenant' : update tenant version 1
253
+ // HEADER : X-Auth-Token = <User token>
254
+ // body : {
255
+ // "tenant": {
256
+ // "id": <id> => key is "yrn:yahoo:::<tenant>:id"
257
+ // this value type must be string.
258
+ // "desc": <description> => value for "yrn:yahoo:::<tenant>:desc"
259
+ // thix value type must be string.
260
+ // "display": <display name> => key is "yrn:yahoo:::<tenant>:display"
261
+ // thix value type must be string.
262
+ // "users": <user> or [user, ...] => key is "yrn:yahoo::::user:<user>"
263
+ // specify adding user array which is yrn path as "yrn:yahoo::::user:<user>" or "user name"
264
+ // }
265
+ // }
266
+ //
267
+ // response status code : 200 or 4xx/5xx
268
+ // response body : {
269
+ // result: true/false
270
+ // message: messages
271
+ // }
272
+ //
273
+ // Update existed tenant as <K2HR3 cluster LOCAL> tenant.
274
+ //
275
+ // [NOTE]
276
+ // If the <K2HR3 cluster LOCAL> tenant does not exist, this repsponses an error.
277
+ // Tenant names must start with "local@"(if not set it, this prefix adds automatically for search).
278
+ // The <User Token> user must be included in the tenant's user list.
279
+ //
280
+ // Specify the user by YRN full path or user name.
281
+ // If the user indicated by <User Token> does not exist, it will be added.
282
+ //
283
+ router.post('/', function(req, res, next) // eslint-disable-line no-unused-vars
284
+ {
285
+ r3logger.dlog('CALL:', req.method, req.url);
286
+
287
+ res.type('application/json; charset=utf-8');
288
+
289
+ var result = {result: true, message: null};
290
+
291
+ if( !apiutil.isSafeEntity(req) ||
292
+ !apiutil.isSafeEntity(req.baseUrl) ||
293
+ !apiutil.isSafeEntity(req.body) ||
294
+ !apiutil.isSafeEntity(req.body.tenant) )
295
+ {
296
+ result.result = false;
297
+ result.message = 'POST body does not have tenant data';
298
+ r3logger.elog(result.message);
299
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
300
+ return;
301
+ }
302
+
303
+ //------------------------------
304
+ // check common parameters(token, tenant etc)
305
+ //------------------------------
306
+ var resobj = rawParseBaseParamInRequest(req);
307
+ if(!resobj.result){
308
+ result.result = resobj.result;
309
+ result.message = resobj.message;
310
+ r3logger.elog(resobj.message);
311
+ resutil.errResponse(req, res, resobj.status_code, result);
312
+ return;
313
+ }
314
+ var comparam = resobj.parameters;
315
+
316
+ //------------------------------
317
+ // check token type
318
+ //------------------------------
319
+ if('user' !== comparam.token_type){
320
+ result.result = false;
321
+ result.message = 'POST request tenant must specify <User Token>';
322
+ r3logger.elog(result.message);
323
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
324
+ return;
325
+ }
326
+
327
+ //------------------------------
328
+ // check arguments
329
+ //------------------------------
330
+ var is_create = true;
331
+ var tenant_name = null;
332
+ var tenant_id = null;
333
+ var tenant_desc = null;
334
+ var tenant_display = null;
335
+ var tenant_users = null;
336
+
337
+ if(!apiutil.isSafeString(comparam.tenant_name)){
338
+ //
339
+ // Create mode
340
+ //
341
+ is_create = true;
342
+ tenant_name = apiutil.getSafeString(req.body.tenant.name).toLowerCase();
343
+ tenant_id = apiutil.getStrUuid4(); // Create new id here.
344
+
345
+ if(!apiutil.isSafeString(tenant_name)){
346
+ result.result = false;
347
+ result.message = 'POST request tenant body does not have tenant.name string object.';
348
+ r3logger.elog(result.message);
349
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
350
+ return;
351
+ }
352
+ if(0 !== tenant_name.indexOf(comparam.keys.VALUE_PREFIX_LOCAL_TENANT)){
353
+ // Not have prefix("local@")
354
+ tenant_name = comparam.keys.VALUE_PREFIX_LOCAL_TENANT + tenant_name;
355
+ }
356
+ }else{
357
+ //
358
+ // Update mode
359
+ //
360
+ is_create = false;
361
+ tenant_name = comparam.tenant_name;
362
+ tenant_id = apiutil.getSafeString(req.body.tenant.id);
363
+ if(!apiutil.isSafeString(tenant_id)){
364
+ result.result = false;
365
+ result.message = 'POST request tenant(' + tenant_name + ') body does not have tenant.id string object.';
366
+ r3logger.elog(result.message);
367
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
368
+ return;
369
+ }
370
+ }
371
+
372
+ if(apiutil.isSafeString(req.body.tenant.desc)){
373
+ tenant_desc = apiutil.getSafeString(req.body.tenant.desc);
374
+ }else{
375
+ tenant_desc = 'K2HR3 Cluster Local tenant';
376
+ }
377
+
378
+ if(apiutil.isSafeString(req.body.tenant.display)){
379
+ tenant_display = apiutil.getSafeString(req.body.tenant.display);
380
+ }else{
381
+ tenant_display = tenant_name;
382
+ }
383
+
384
+ //
385
+ // Check users
386
+ //
387
+ if(apiutil.getSafeString(req.body.tenant.users)){
388
+ tenant_users = [req.body.tenant.users.trim()];
389
+ }else{
390
+ tenant_users = apiutil.getSafeArray(req.body.tenant.users);
391
+ }
392
+ if(is_create){
393
+ // add own user
394
+ apiutil.tryAddStringToArray(tenant_users, comparam.user_name);
395
+ }else{
396
+ if(!apiutil.findStringInArray(tenant_users, comparam.user_name)){
397
+ result.result = false;
398
+ result.message = 'POST request tenant(' + tenant_name + ') does not allow user(' + comparam.user_name + ').';
399
+ r3logger.elog(result.message);
400
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
401
+ return;
402
+ }
403
+ }
404
+ if(apiutil.isArray(tenant_users)){
405
+ tenant_users.sort();
406
+ }
407
+
408
+ //------------------------------
409
+ // Processing
410
+ //------------------------------
411
+ resobj = rawCreateUpdateTenant(is_create, comparam.user_name, tenant_name, tenant_id, tenant_desc, tenant_display, tenant_users);
412
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || !apiutil.isSafeEntity(resobj.rescode) || false === resobj.result){
413
+ result.result = false;
414
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
415
+ result.message = 'POST request failed to update tenant by ' + resobj.message;
416
+ }else{
417
+ result.message = 'POST request failed to update tenant by unknown reason';
418
+ }
419
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeEntity(resobj.rescode)){
420
+ resutil.errResponse(req, res, resobj.rescode, result);
421
+ }else{
422
+ resutil.errResponse(req, res, 500, result); // 500: Internal error
423
+ }
424
+ r3logger.elog(result.message);
425
+ return;
426
+ }
427
+
428
+ r3logger.dlog('succeed : create/update tenant(' + tenant_name + ') by user(' + comparam.user_name + ')');
429
+ res.status(resobj.rescode); // 200 or 201
430
+ res.send(JSON.stringify(result));
431
+ });
432
+
433
+ //=========================================================
434
+ // Router PUT
435
+ //=========================================================
436
+ //
437
+ // Mountpath : '/v1/tenant'
438
+ //
439
+ //---------------------------------------------------------
440
+ // [PUT] No tenant path
441
+ //---------------------------------------------------------
442
+ // PUT '/v1/tenant' : create tenant version 1
443
+ // HEADER : X-Auth-Token = <User token>
444
+ // url argument :
445
+ // "name": <tenant> => key is "yrn:yahoo:::<tenant>"
446
+ // <tenant> must include the prefix "R3CLUSTERLOCAL-".
447
+ // "desc": <description> => value for "yrn:yahoo:::<tenant>:desc"
448
+ // thix value type must be string.
449
+ // "display": <display name> => key is "yrn:yahoo:::<tenant>:display"
450
+ // thix value type must be string.
451
+ // "users": <user> or [user, ...] => key is "yrn:yahoo::::user:<user>"
452
+ // specify adding user array which is yrn path as "yrn:yahoo::::user:<user>" or "user name"
453
+ //
454
+ // response status code : 201 or 4xx/5xx
455
+ // response body : {
456
+ // result: true/false
457
+ // message: messages
458
+ // }
459
+ //
460
+ // Create a tenant as <K2HR3 cluster LOCAL> tenant.
461
+ //
462
+ // [NOTE]
463
+ // If the <K2HR3 cluster LOCAL> tenant already exists, this repsponses an error.
464
+ //
465
+ // Tenant names must start with "R3CLUSTERLOCAL-".
466
+ // Specify the user by YRN full path or user name.
467
+ // If the user indicated by <User Token> does not exist, it will be added.
468
+ // New tenant id("R3CLUSTERLOCAL-xxxxxx") is set automatically.
469
+ //
470
+ //---------------------------------------------------------
471
+ // [PUT] With tenant path
472
+ //---------------------------------------------------------
473
+ // PUT '/v1/tenant/tenant' : update tenant version 1
474
+ // HEADER : X-Auth-Token = <User token>
475
+ // url argument :
476
+ // "id": <id> => key is "yrn:yahoo:::<tenant>:id"
477
+ // this value type must be string.
478
+ // "desc": <description> => value for "yrn:yahoo:::<tenant>:desc"
479
+ // thix value type must be string.
480
+ // "display": <display name> => key is "yrn:yahoo:::<tenant>:display"
481
+ // thix value type must be string.
482
+ // "users": <user> or [user, ...] => key is "yrn:yahoo::::user:<user>"
483
+ // specify adding user array which is yrn path as "yrn:yahoo::::user:<user>" or "user name"
484
+ //
485
+ // response status code : 200 or 4xx/5xx
486
+ // response body : {
487
+ // result: true/false
488
+ // message: messages
489
+ // }
490
+ //
491
+ // Update existed tenant as <K2HR3 cluster LOCAL> tenant.
492
+ //
493
+ // [NOTE]
494
+ // If the <K2HR3 cluster LOCAL> tenant does not exist, this repsponses an error.
495
+ // The <User Token> user must be included in the tenant's user list.
496
+ //
497
+ // Specify the user by YRN full path or user name.
498
+ // If the user indicated by <User Token> does not exist, it will be added.
499
+ //
500
+ router.put('/', function(req, res, next) // eslint-disable-line no-unused-vars
501
+ {
502
+ r3logger.dlog('CALL:', req.method, req.url);
503
+
504
+ res.type('application/json; charset=utf-8');
505
+
506
+ var result = {result: true, message: null};
507
+
508
+ if( !apiutil.isSafeEntity(req) ||
509
+ !apiutil.isSafeEntity(req.baseUrl) ||
510
+ !apiutil.isSafeEntity(req.query) )
511
+ {
512
+ result.result = false;
513
+ result.message = 'PUT request is something wrong';
514
+ r3logger.elog(result.message);
515
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
516
+ return;
517
+ }
518
+
519
+ //------------------------------
520
+ // check common parameters(token, tenant etc)
521
+ //------------------------------
522
+ var resobj = rawParseBaseParamInRequest(req);
523
+ if(!resobj.result){
524
+ result.result = resobj.result;
525
+ result.message = resobj.message;
526
+ r3logger.elog(resobj.message);
527
+ resutil.errResponse(req, res, resobj.status_code, result);
528
+ return;
529
+ }
530
+ var comparam = resobj.parameters;
531
+
532
+ //------------------------------
533
+ // check token type
534
+ //------------------------------
535
+ if('user' !== comparam.token_type){
536
+ result.result = false;
537
+ result.message = 'PUT request tenant must specify <User Token>';
538
+ r3logger.elog(result.message);
539
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
540
+ return;
541
+ }
542
+
543
+ //------------------------------
544
+ // check arguments
545
+ //------------------------------
546
+ var is_create = true;
547
+ var tenant_name = null;
548
+ var tenant_id = null;
549
+ var tenant_desc = null;
550
+ var tenant_display = null;
551
+ var tenant_users = null;
552
+
553
+ if(!apiutil.isSafeString(comparam.tenant_name)){
554
+ //
555
+ // Create mode
556
+ //
557
+ is_create = true;
558
+ tenant_name = apiutil.getSafeString(req.query.name).toLowerCase();
559
+ tenant_id = apiutil.getStrUuid4(); // Create new id here.
560
+
561
+ if(!apiutil.isSafeString(tenant_name)){
562
+ result.result = false;
563
+ result.message = 'PUT request tenant body does not have tenant.name string object.';
564
+ r3logger.elog(result.message);
565
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
566
+ return;
567
+ }
568
+ if(0 !== tenant_name.indexOf(comparam.keys.VALUE_PREFIX_LOCAL_TENANT)){
569
+ // Not have prefix("local@")
570
+ tenant_name = comparam.keys.VALUE_PREFIX_LOCAL_TENANT + tenant_name;
571
+ }
572
+ }else{
573
+ //
574
+ // Update mode
575
+ //
576
+ is_create = false;
577
+ tenant_name = comparam.tenant_name;
578
+ tenant_id = apiutil.getSafeString(req.query.id);
579
+ if(!apiutil.isSafeString(tenant_id)){
580
+ result.result = false;
581
+ result.message = 'PUT request tenant(' + tenant_name + ') body does not have tenant.id string object.';
582
+ r3logger.elog(result.message);
583
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
584
+ return;
585
+ }
586
+ }
587
+
588
+ if(apiutil.isSafeString(req.query.desc)){
589
+ tenant_desc = apiutil.getSafeString(req.query.desc);
590
+ }else{
591
+ tenant_desc = 'K2HR3 Cluster Local tenant';
592
+ }
593
+
594
+ if(apiutil.isSafeString(req.query.display)){
595
+ tenant_display = apiutil.getSafeString(req.query.display);
596
+ }else{
597
+ tenant_display = tenant_name;
598
+ }
599
+
600
+ //
601
+ // Check users
602
+ //
603
+ if(apiutil.checkSimpleJSON(req.query.users)){
604
+ tenant_users = apiutil.parseJSON(req.query.users);
605
+ if(!apiutil.isArray(tenant_users) && apiutil.isSafeString(tenant_users)){
606
+ tenant_users = [tenant_users];
607
+ }else if(!apiutil.isArray(tenant_users)){
608
+ tenant_users = [];
609
+ }
610
+ }else if(apiutil.isArray(req.query.users)){
611
+ tenant_users = req.query.users;
612
+ }else if(apiutil.isSafeString(req.query.users)){
613
+ tenant_users = [req.query.users];
614
+ }else{
615
+ tenant_users = [];
616
+ }
617
+ if(is_create){
618
+ // add own user
619
+ apiutil.tryAddStringToArray(tenant_users, comparam.user_name);
620
+ }else{
621
+ if(!apiutil.findStringInArray(tenant_users, comparam.user_name)){
622
+ result.result = false;
623
+ result.message = 'PUT request tenant(' + tenant_name + ') does not allow user(' + comparam.user_name + ').';
624
+ r3logger.elog(result.message);
625
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
626
+ return;
627
+ }
628
+ }
629
+ if(apiutil.isArray(tenant_users)){
630
+ tenant_users.sort();
631
+ }
632
+
633
+ //------------------------------
634
+ // Processing
635
+ //------------------------------
636
+ resobj = rawCreateUpdateTenant(is_create, comparam.user_name, tenant_name, tenant_id, tenant_desc, tenant_display, tenant_users);
637
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || !apiutil.isSafeEntity(resobj.rescode) || false === resobj.result){
638
+ result.result = false;
639
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
640
+ result.message = 'PUT request failed to update tenant by ' + resobj.message;
641
+ }else{
642
+ result.message = 'PUT request failed to update tenant by unknown reason';
643
+ }
644
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeEntity(resobj.rescode)){
645
+ resutil.errResponse(req, res, resobj.rescode, result);
646
+ }else{
647
+ resutil.errResponse(req, res, 500, result); // 500: Internal error
648
+ }
649
+ r3logger.elog(result.message);
650
+ return;
651
+ }
652
+
653
+ r3logger.dlog('succeed : create/update tenant(' + tenant_name + ') by user(' + comparam.user_name + ')');
654
+ res.status(resobj.rescode); // 200 or 201
655
+ res.send(JSON.stringify(result));
656
+ });
657
+
658
+ //=========================================================
659
+ // Router GET
660
+ //=========================================================
661
+ //
662
+ // Mountpath : '/v1/tenant{/tenant}'
663
+ //
664
+ //---------------------------------------------------------
665
+ // [GET] No tenant path
666
+ //---------------------------------------------------------
667
+ // GET '/v1/tenant' : get tenant list version 1
668
+ // HEADER : X-Auth-Token = <User token>
669
+ // URL arguments : expand = "true"(default) or "false"
670
+ // response status code : 200 or 4xx/5xx
671
+ // response : nothing
672
+ // response body : {
673
+ // result: true/false,
674
+ // message: null or error message string
675
+ // tenants: [
676
+ // {
677
+ // name: "string",
678
+ // id: "string",
679
+ // desc: "string",
680
+ // display: "string",
681
+ // user: array[users...]
682
+ // },
683
+ // ...
684
+ // ]
685
+ // }
686
+ // or
687
+ // {
688
+ // result: true/false,
689
+ // message: null or error message string
690
+ // tenants: [
691
+ // "tenant",
692
+ // ...
693
+ // ]
694
+ // }
695
+ //
696
+ // This mount point retrieves a list of tenants and information about each.
697
+ //
698
+ //---------------------------------------------------------
699
+ // [GET] With tenant path
700
+ //---------------------------------------------------------
701
+ // GET '/v1/tenant/<tenant>' : get tenant information on version 1
702
+ // HEADER : X-Auth-Token = <User token>
703
+ // URL arguments : nothing
704
+ // response status code : 200 or 4xx/5xx
705
+ // response : nothing
706
+ // response body : {
707
+ // result: true/false,
708
+ // message: null or error message string
709
+ // tenant: {
710
+ // name: "string",
711
+ // id: "string",
712
+ // desc: "string",
713
+ // display: "string",
714
+ // user: array[users...]
715
+ // }
716
+ // }
717
+ //
718
+ router.get('/', function(req, res, next)
719
+ {
720
+ r3logger.dlog('CALL:', req.method, req.url);
721
+
722
+ if('GET' !== req.method){
723
+ // HEAD request comes here, so it should be routed to head function.
724
+ next();
725
+ return;
726
+ }
727
+
728
+ res.type('application/json; charset=utf-8');
729
+
730
+ var result = {result: true, message: null};
731
+
732
+ if( !apiutil.isSafeEntity(req) ||
733
+ !apiutil.isSafeEntity(req.baseUrl) )
734
+ {
735
+ result.result = false;
736
+ result.message = 'GET request is something wrong';
737
+ r3logger.elog(result.message);
738
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
739
+ return;
740
+ }
741
+
742
+ //------------------------------
743
+ // check common parameters(token, tenant etc)
744
+ //------------------------------
745
+ var resobj = rawParseBaseParamInRequest(req);
746
+ if(!resobj.result){
747
+ result.result = resobj.result;
748
+ result.message = resobj.message;
749
+ r3logger.elog(resobj.message);
750
+ resutil.errResponse(req, res, resobj.status_code, result);
751
+ return;
752
+ }
753
+ var comparam = resobj.parameters;
754
+
755
+ //------------------------------
756
+ // check token type
757
+ //------------------------------
758
+ if('user' !== comparam.token_type){
759
+ result.result = false;
760
+ result.message = 'GET request tenant must specify <User Token>';
761
+ r3logger.elog(result.message);
762
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
763
+ return;
764
+ }
765
+
766
+ //------------------------------
767
+ // Processing
768
+ //------------------------------
769
+ if(!apiutil.isSafeString(comparam.tenant_name)){
770
+ //
771
+ // List mode
772
+ //
773
+ var keys = r3keys(comparam.user_name);
774
+
775
+ //
776
+ // Check expand type
777
+ //
778
+ var is_expand = true;
779
+ if(apiutil.isSafeEntity(req.query) && apiutil.isSafeString(req.query.expand)){
780
+ if(apiutil.compareCaseString(keys.VALUE_TRUE, req.query.expand)){
781
+ is_expand = true;
782
+ }else if(apiutil.compareCaseString(keys.VALUE_FALSE, req.query.expand)){
783
+ is_expand = false;
784
+ }else{
785
+ result.result = false;
786
+ result.message = 'GET expand url argument parameter(' + JSON.stringify(req.query.expand) + ') is wrong, it must be ' + keys.VALUE_TRUE + ' or ' + keys.VALUE_FALSE + '.';
787
+ r3logger.elog(result.message);
788
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
789
+ return;
790
+ }
791
+ }
792
+
793
+ //
794
+ // Get list
795
+ //
796
+ resobj = k2hr3.listLocalTenant(comparam.user_name, is_expand);
797
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || !apiutil.isArray(resobj.tenants) || false === resobj.result){
798
+ result.result = false;
799
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
800
+ result.message = 'GET request failed to update tenant by ' + resobj.message;
801
+ }else{
802
+ result.message = 'GET request failed to update tenant by unknown reason';
803
+ }
804
+ r3logger.elog(result.message);
805
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
806
+ return;
807
+ }
808
+ result.tenants = resobj.tenants;
809
+
810
+ }else{
811
+ //
812
+ // One tenant
813
+ //
814
+ resobj = k2hr3.findTenant(comparam.tenant_name);
815
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || !apiutil.isSafeEntity(resobj.tenant) || false === resobj.result){
816
+ result.result = false;
817
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
818
+ result.message = 'GET request failed to update tenant by ' + resobj.message;
819
+ }else{
820
+ result.message = 'GET request failed to update tenant by unknown reason';
821
+ }
822
+ r3logger.elog(result.message);
823
+ resutil.errResponse(req, res, 400, result); // 400: Bad Request
824
+ return;
825
+ }
826
+ result.tenant = resobj.tenant;
827
+ }
828
+
829
+ r3logger.dlog('succeed : get tenant(s) by user(' + comparam.user_name + ')');
830
+ res.status(200); // 200: OK
831
+ res.send(JSON.stringify(result));
832
+ });
833
+
834
+ //=========================================================
835
+ // Router HEAD
836
+ //=========================================================
837
+ //
838
+ // Mountpath : '/v1/tenant/tenant'
839
+ //
840
+ // HEAD '/v1/tenant/<tenant>' : head tenant on version 1
841
+ // HEADER : X-Auth-Token = <User token>
842
+ // response status code : 204 or 4xx/5xx
843
+ // response body : nothing
844
+ //
845
+ // This mount point is an API for checking the existence of a tenant.
846
+ // If the tenant is given, this returns a 204 if that tenant exists and is allowed to be seen.
847
+ // If no tenant is given, returns 204 if at least one target tenant exists.
848
+ //
849
+ router.head('/', function(req, res, next)
850
+ {
851
+ r3logger.dlog('CALL:', req.method, req.url);
852
+
853
+ if('HEAD' !== req.method){
854
+ // If other method request comes here, so it should be routed another function.
855
+ next();
856
+ return;
857
+ }
858
+ res.type('application/json; charset=utf-8');
859
+
860
+ if( !apiutil.isSafeEntity(req) ||
861
+ !apiutil.isSafeEntity(req.baseUrl) )
862
+ {
863
+ r3logger.elog('HEAD request is something wrong');
864
+ resutil.errResponse(req, res, 400); // 400: Bad Request
865
+ return;
866
+ }
867
+
868
+ //------------------------------
869
+ // check common parameters(token, tenant etc)
870
+ //------------------------------
871
+ var resobj = rawParseBaseParamInRequest(req);
872
+ if(!resobj.result){
873
+ r3logger.elog(resobj.message);
874
+ resutil.errResponse(req, res, resobj.status_code);
875
+ return;
876
+ }
877
+ var comparam = resobj.parameters;
878
+
879
+ //------------------------------
880
+ // check token type
881
+ //------------------------------
882
+ if('user' !== comparam.token_type){
883
+ r3logger.elog('HEAD request tenant must specify <User Token>');
884
+ resutil.errResponse(req, res, 400); // 400: Bad Request
885
+ return;
886
+ }
887
+
888
+ //------------------------------
889
+ // Check uri paths(tenant name)
890
+ //------------------------------
891
+ if(!apiutil.isSafeString(comparam.tenant_name)){
892
+ r3logger.elog('HEAD request tenant must specify <tenant> path');
893
+ resutil.errResponse(req, res, 400); // 400: Bad Request
894
+ return;
895
+ }
896
+
897
+ //------------------------------
898
+ // Processing
899
+ //------------------------------
900
+ resobj = k2hr3.findTenant(comparam.tenant_name);
901
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || !apiutil.isSafeEntity(resobj.tenant) || false === resobj.result){
902
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
903
+ r3logger.elog('HEAD request failed to update tenant by ' + resobj.message);
904
+ }else{
905
+ r3logger.elog('HEAD request failed to update tenant by unknown reason');
906
+ }
907
+ resutil.errResponse(req, res, 400); // 400: Bad Request
908
+ return;
909
+ }
910
+
911
+ r3logger.dlog('HEAD request succeed - check tenant(' + comparam.tenant_name + ') exists');
912
+ res.status(204); // 204: No Content
913
+ res.send();
914
+ });
915
+
916
+ //=========================================================
917
+ // Router DELETE
918
+ //=========================================================
919
+ //
920
+ // Mountpath : '/v1/tenant/<tenant>'
921
+ //
922
+ // DELETE '/v1/tenant/<tenant>' : delete tenant on version 1
923
+ // HEADER : X-Auth-Token = <User token>
924
+ // url argument : "id": <id> => key is "yrn:yahoo:::<tenant>:id"
925
+ // response status code : 204 or 4xx/5xx
926
+ // response body : nothing
927
+ //
928
+ // This mount point deletes the specified <K2HR3 cluster LOCAL> tenant.
929
+ //
930
+ // [NOTE]
931
+ // Only users registered in the tenant to be deleted can delete this tenant.
932
+ //
933
+ router.delete('/', function(req, res, next) // eslint-disable-line no-unused-vars
934
+ {
935
+ r3logger.dlog('CALL:', req.method, req.url);
936
+
937
+ res.type('application/json; charset=utf-8');
938
+
939
+ if( !apiutil.isSafeEntity(req) ||
940
+ !apiutil.isSafeEntity(req.baseUrl) )
941
+ {
942
+ r3logger.elog('DELETE request or url or query is wrong');
943
+ resutil.errResponse(req, res, 400); // 400: Bad Request
944
+ return;
945
+ }
946
+
947
+ //------------------------------
948
+ // check common parameters(token, tenant etc)
949
+ //------------------------------
950
+ var resobj = rawParseBaseParamInRequest(req);
951
+ if(!resobj.result){
952
+ r3logger.elog(resobj.message);
953
+ resutil.errResponse(req, res, resobj.status_code);
954
+ return;
955
+ }
956
+ var comparam = resobj.parameters;
957
+
958
+ //------------------------------
959
+ // check token type
960
+ //------------------------------
961
+ if('user' !== comparam.token_type){
962
+ r3logger.elog('DELETE request tenant must specify <User Token>');
963
+ resutil.errResponse(req, res, 400); // 400: Bad Request
964
+ return;
965
+ }
966
+
967
+ //------------------------------
968
+ // Check uri paths(tenant name)
969
+ //------------------------------
970
+ if(!apiutil.isSafeString(comparam.tenant_name)){
971
+ r3logger.elog('DELETE request tenant must specify <tenant> path');
972
+ resutil.errResponse(req, res, 400); // 400: Bad Request
973
+ return;
974
+ }
975
+
976
+ //------------------------------
977
+ // Check argments(id)
978
+ //------------------------------
979
+ var tenant_id = apiutil.getSafeString(req.query.id);
980
+ if(!apiutil.isSafeString(tenant_id)){
981
+ r3logger.elog('DELETE request id must specify in argument');
982
+ resutil.errResponse(req, res, 400); // 400: Bad Request
983
+ return;
984
+ }
985
+
986
+ //------------------------------
987
+ // Processing
988
+ //------------------------------
989
+ resobj = k2hr3.removeUserFromLocalTenant(comparam.tenant_name, comparam.user_name, tenant_id);
990
+ if(!apiutil.isSafeEntity(resobj) || !apiutil.isSafeEntity(resobj.result) || false === resobj.result){
991
+ if(apiutil.isSafeEntity(resobj) && apiutil.isSafeString(resobj.message)){
992
+ r3logger.elog('DELETE request failed to remove user from tenant by ' + resobj.message);
993
+ }else{
994
+ r3logger.elog('DELETE request failed to remove user from tenant by unknown reason');
995
+ }
996
+ resutil.errResponse(req, res, 400); // 400: Bad Request
997
+ return;
998
+ }
999
+
1000
+ r3logger.dlog('DELETE request succeed - remove user from tenant');
1001
+ res.status(204); // 204: No Content
1002
+ res.send();
1003
+ });
1004
+
1005
+ module.exports = router;
1006
+
1007
+ /*
1008
+ * Local variables:
1009
+ * tab-width: 4
1010
+ * c-basic-offset: 4
1011
+ * End:
1012
+ * vim600: noexpandtab sw=4 ts=4 fdm=marker
1013
+ * vim<600: noexpandtab sw=4 ts=4
1014
+ */