soajs.controller 4.2.42 → 4.3.0

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 (37) hide show
  1. package/.github/workflows/ci.yml +43 -0
  2. package/.github/workflows/release.yml +94 -0
  3. package/ARCHITECTURE.md +193 -0
  4. package/Gruntfile.js +1 -1
  5. package/docs/configuration-custom-registry.md +421 -0
  6. package/docs/configuration-services-config.md +275 -0
  7. package/docs/data-flow.md +138 -0
  8. package/docs/deployment.md +205 -0
  9. package/docs/external-integrations.md +159 -0
  10. package/docs/gateway-routes.md +259 -0
  11. package/docs/maintenance-routes.md +308 -0
  12. package/docs/middleware/awareness.md +120 -0
  13. package/docs/middleware/cors.md +88 -0
  14. package/docs/middleware/enhancer.md +75 -0
  15. package/docs/middleware/favicon.md +36 -0
  16. package/docs/middleware/gotoService.md +146 -0
  17. package/docs/middleware/ip2ban.md +70 -0
  18. package/docs/middleware/key.md +196 -0
  19. package/docs/middleware/keyACL.md +139 -0
  20. package/docs/middleware/lastSeen.md +111 -0
  21. package/docs/middleware/maintenanceMode.md +81 -0
  22. package/docs/middleware/mt.md +568 -0
  23. package/docs/middleware/oauth.md +173 -0
  24. package/docs/middleware/response.md +106 -0
  25. package/docs/middleware/soajs.md +71 -0
  26. package/docs/middleware/traffic.md +190 -0
  27. package/docs/middleware/url.md +102 -0
  28. package/docs/multi-tenant-security.md +183 -0
  29. package/docs/request-flow.md +230 -0
  30. package/docs/service-discovery.md +167 -0
  31. package/mw/key/index.js +16 -1
  32. package/mw/mt/urac.js +21 -1
  33. package/mw/mt/utils.js +2 -0
  34. package/package.json +13 -13
  35. package/test/unit/mw/key/index.js +150 -0
  36. package/test/unit/mw/mt/urac.js +157 -4
  37. package/.travis.yml +0 -72
@@ -0,0 +1,568 @@
1
+ # MT (Multi-Tenant) Middleware
2
+
3
+ **File:** `mw/mt/index.js`
4
+
5
+ **Order:** 14
6
+
7
+ The MT middleware handles multi-tenant security checks, URAC integration, and prepares the injection header for target services.
8
+
9
+ ## Purpose
10
+
11
+ - Enforces multi-tenant security pipeline
12
+ - Loads tenant and application context
13
+ - Integrates with URAC for user profiles
14
+ - Prepares `soajsinjectobj` header for downstream services
15
+
16
+ ## Flow
17
+
18
+ ```
19
+ Incoming Request
20
+ │
21
+ ▼
22
+ ┌─────────────────────────┐
23
+ │ Is Proxy Route? │ Skip security for proxy
24
+ └────────┬────────────────┘
25
+ │
26
+ ├─── Yes ──▶ Execute OAuth check → next()
27
+ │
28
+ ▼
29
+ ┌─────────────────────────┐
30
+ │ External Key │ Based on service config
31
+ │ Required? │
32
+ └────────┬────────────────┘
33
+ │
34
+ ├─── No ──▶ Mark as public API → OAuth check
35
+ │
36
+ ▼
37
+ ┌─────────────────────────┐
38
+ │ Load Tenant Context │ From keyObj
39
+ └────────┬────────────────┘
40
+ │
41
+ ▼
42
+ ┌─────────────────────────┐
43
+ │ Security Pipeline │ Waterfall checks:
44
+ │ │ 1. IP Whitelist
45
+ │ │ 2. Geo Check
46
+ │ │ 3. Device Check
47
+ │ │ 4. ACL Check
48
+ │ │ 5. OAuth Check (if required)
49
+ │ │ 6. URAC Check
50
+ │ │ 7. URAC ACL Check
51
+ │ │ 8. Service Check
52
+ │ │ 9. API Check
53
+ └────────┬────────────────┘
54
+ │
55
+ ├─── Any check fails ──▶ Error response
56
+ │
57
+ ▼
58
+ ┌─────────────────────────┐
59
+ │ Build Inject Object │ Tenant, key, app, user info
60
+ └────────┬────────────────┘
61
+ │
62
+ ▼
63
+ ┌─────────────────────────┐
64
+ │ Resolve InterConnect │ Service-to-service awareness
65
+ │ (if configured) │
66
+ └────────┬────────────────┘
67
+ │
68
+ ▼
69
+ ┌─────────────────────────┐
70
+ │ Set soajsinjectobj │ req.headers.soajsinjectobj = JSON
71
+ │ Header │
72
+ └────────┬────────────────┘
73
+ │
74
+ ▼
75
+ next()
76
+ ```
77
+
78
+ ## Security Pipeline (utils.js)
79
+
80
+ | Order | Check | Description |
81
+ |-------|-------|-------------|
82
+ | 1 | `ipWhitelist` | Validates IP against whitelist |
83
+ | 2 | `securityGeoCheck` | Validates geographic location |
84
+ | 3 | `securityDeviceCheck` | Validates device fingerprint |
85
+ | 4 | `aclCheck` | Loads service-level ACL |
86
+ | 5 | `oauthCheck` | Validates OAuth token (if required) |
87
+ | 6 | `uracCheck` | Loads user profile from URAC |
88
+ | 7 | `aclUrackCheck` | Overrides ACL with user-specific ACL |
89
+ | 8 | `serviceCheck` | Validates service access |
90
+ | 9 | `apiCheck` | Validates API access |
91
+
92
+ ---
93
+
94
+ ## Waterfall Check Details
95
+
96
+ ### 1. ipWhitelist
97
+
98
+ **Purpose:** Check if the client IP is in a trusted whitelist, optionally bypassing ACL and OAuth checks.
99
+
100
+ **Location:** `mw/mt/utils.js:166`
101
+
102
+ **Configuration:** `registry.custom.gateway.value.mt.whitelist`
103
+
104
+ ```javascript
105
+ {
106
+ "mt": {
107
+ "whitelist": {
108
+ "ips": ["10.0.0.0/8", "192.168.1.0/24", "127.0.0.1"],
109
+ "acl": true, // Skip ACL checks for whitelisted IPs
110
+ "oauth": true // Skip OAuth checks for whitelisted IPs
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
116
+ **Behavior:**
117
+ - Uses CIDR notation (Netmask) for IP range matching
118
+ - Sets `obj.skipACL = true` if `whitelist.acl` is enabled and IP matches
119
+ - Sets `obj.skipOAUTH = true` if `whitelist.oauth` is enabled and IP matches
120
+ - Netmask objects are cached for performance
121
+
122
+ **Use Cases:**
123
+ - Trusted internal services calling the gateway
124
+ - Development/testing environments
125
+ - Load balancer health checks
126
+
127
+ ---
128
+
129
+ ### 2. securityGeoCheck
130
+
131
+ **Purpose:** Validates the client IP against geographic allow/deny lists configured on the tenant key.
132
+
133
+ **Location:** `mw/mt/utils.js:309`
134
+
135
+ **Configuration:** `keyObj.geo`
136
+
137
+ ```javascript
138
+ {
139
+ "geo": {
140
+ "allow": ["10.0.0.0/8", "192.168.0.0/16"], // Only these IPs allowed
141
+ "deny": ["10.0.1.0/24"] // Block these IPs
142
+ }
143
+ }
144
+ ```
145
+
146
+ **Behavior:**
147
+ - **Deny list checked first:** If client IP matches any deny entry → Error 155
148
+ - **Allow list checked second:** If allow list exists and IP doesn't match → Error 155
149
+ - Uses CIDR notation for IP range matching
150
+ - Skipped if `obj.skipACL = true` (from ipWhitelist)
151
+
152
+ **Error Codes:**
153
+ | Code | Description |
154
+ |------|-------------|
155
+ | 155 | Geographic access denied |
156
+
157
+ ---
158
+
159
+ ### 3. securityDeviceCheck
160
+
161
+ **Purpose:** Validates the client device/browser against allow/deny lists configured on the tenant key.
162
+
163
+ **Location:** `mw/mt/utils.js:355`
164
+
165
+ **Configuration:** `keyObj.device`
166
+
167
+ ```javascript
168
+ {
169
+ "device": {
170
+ "allow": [
171
+ { "family": "Chrome" },
172
+ { "family": "Firefox", "major": { "min": "90" } },
173
+ { "family": "Safari", "os": { "family": "Mac OS X" } }
174
+ ],
175
+ "deny": [
176
+ { "family": "IE" },
177
+ { "family": "Chrome", "major": { "max": "80" } }
178
+ ]
179
+ }
180
+ }
181
+ ```
182
+
183
+ **Validation Fields:**
184
+ | Field | Type | Description |
185
+ |-------|------|-------------|
186
+ | `family` | string | Browser family (Chrome, Firefox, Safari, IE, etc.) |
187
+ | `major` | string/object | Major version. Object: `{ "min": "90", "max": "100" }` |
188
+ | `minor` | string/object | Minor version |
189
+ | `patch` | string/object | Patch version |
190
+ | `os.family` | string | OS family (Windows, Mac OS X, Linux, etc.) |
191
+ | `os.major` | string/object | OS major version |
192
+
193
+ **Behavior:**
194
+ - Uses `useragent` library to parse User-Agent header
195
+ - **Deny list checked first:** If device matches any deny entry → Error 156
196
+ - **Allow list checked second:** If allow list exists and device doesn't match → Error 156
197
+ - Wildcard `"*"` matches any value
198
+ - Skipped if `obj.skipACL = true` (from ipWhitelist)
199
+
200
+ **Error Codes:**
201
+ | Code | Description |
202
+ |------|-------------|
203
+ | 156 | Device access denied |
204
+
205
+ ---
206
+
207
+ ### 4. aclCheck
208
+
209
+ **Purpose:** Loads the pre-computed ACL from `serviceParams.finalAcl` into the waterfall object.
210
+
211
+ **Location:** `mw/mt/utils.js:274`
212
+
213
+ **Behavior:**
214
+ - Simple passthrough that loads `obj.req.soajs.controller.serviceParams.finalAcl` into `obj.finalAcl`
215
+ - The ACL was computed earlier in the [keyACL middleware](./keyACL.md)
216
+ - This ACL will be used by subsequent checks (serviceCheck, apiCheck)
217
+
218
+ ---
219
+
220
+ ### 5. oauthCheck
221
+
222
+ **Purpose:** Validates OAuth access tokens for private APIs and handles PIN-locked sessions.
223
+
224
+ **Location:** `mw/mt/utils.js:463`
225
+
226
+ **Prerequisites:**
227
+ - `serviceParam.oauth = true` must be set on the service
228
+ - Skipped if `obj.skipOAUTH = true` (from ipWhitelist)
229
+
230
+ **Public vs Private API Detection:**
231
+ | Scenario | ACL Configuration | Result |
232
+ |----------|-------------------|--------|
233
+ | No access restriction | `access: false` or missing | Public |
234
+ | Service restricted, API public | `access: true`, `apis[path].access: false` | Public |
235
+ | Restricted permission, API public | `apisPermission: "restricted"`, `apis[path].access: false` | Public |
236
+ | Any access restriction | `access: true` or `apis[path].access: true` | Private |
237
+
238
+ **PIN Login Support:**
239
+
240
+ PIN login is a two-factor authentication mechanism where users must enter a PIN after OAuth login.
241
+
242
+ **Configuration:** `registry.custom.oauth.value`
243
+
244
+ ```javascript
245
+ {
246
+ "oauth": {
247
+ "value": {
248
+ "pinWrapper": {
249
+ "servicename": "custom-service",
250
+ "apiname": "/verify-pin"
251
+ },
252
+ "pinWhitelist": {
253
+ "servicename": {
254
+ "get": {
255
+ "apis": ["/public-api"],
256
+ "regex": ["/user/:id/profile"]
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ ```
264
+
265
+ **PIN Flow:**
266
+ 1. User logs in with OAuth → receives token with `pinLocked: true`
267
+ 2. Most APIs blocked (error 145) until PIN verified
268
+ 3. PIN whitelist allows specific APIs before verification
269
+ 4. After PIN verification, `pinLogin: true` set, full access granted
270
+
271
+ **Error Codes:**
272
+ | Code | Description |
273
+ |------|-------------|
274
+ | 145 | PIN verification required |
275
+
276
+ ---
277
+
278
+ ### 6. uracCheck
279
+
280
+ **Purpose:** Initializes the URAC driver to load user profile, groups, ACL, and configuration.
281
+
282
+ **Location:** `mw/mt/utils.js:624`
283
+
284
+ **Behavior:**
285
+ - Skipped if `obj.skipOAUTH = true` or no bearer token present
286
+ - Creates `req.soajs.uracDriver` instance
287
+ - Handles **roaming** across environments (e.g., dashboard token used in dev environment)
288
+
289
+ **Roaming Support:**
290
+
291
+ Roaming allows users authenticated in a "sensitive" environment (default: `dashboard`) to access other environments.
292
+
293
+ **Configuration:** Environment variable `SOAJS_SENSITIVE_ENVS`
294
+ ```bash
295
+ SOAJS_SENSITIVE_ENVS='["dashboard", "production"]'
296
+ ```
297
+
298
+ **Roaming Flow:**
299
+ 1. Check if token's environment is in sensitive list AND differs from current
300
+ 2. Load tenant data for the token's client ID
301
+ 3. Load registry for the token's environment
302
+ 4. Set `req.soajs.tenant.roaming` with cross-environment context
303
+
304
+ **Token Account Validation:**
305
+
306
+ Validates that user account hasn't changed since token was issued.
307
+
308
+ **Configuration:** `keyObj.config.gateway.validateTokenAccount`
309
+
310
+ ```javascript
311
+ {
312
+ "gateway": {
313
+ "validateTokenAccount": {
314
+ "email": true, // Invalidate if email changed
315
+ "phone": true // Invalidate if phone changed
316
+ }
317
+ }
318
+ }
319
+ ```
320
+
321
+ **Error Codes:**
322
+ | Code | Description |
323
+ |------|-------------|
324
+ | 146 | User profile not found or account changed |
325
+ | 170 | Roaming context failed to load |
326
+
327
+ ---
328
+
329
+ ### 7. aclUrackCheck
330
+
331
+ **Purpose:** Overrides the tenant/package ACL with user-specific ACL from URAC profile.
332
+
333
+ **Location:** `mw/mt/utils.js:217`
334
+
335
+ **Behavior:**
336
+ - Skipped if `obj.skipOAUTH = true` or no URAC driver
337
+ - Retrieves user's personal ACL via `uracDriver.getAcl()`
338
+ - If user ACL exists, it **replaces** the tenant/package ACL
339
+ - Handles version resolution when no version requested
340
+ - Processes method-specific ACL (GET, POST, etc.)
341
+
342
+ **ACL Structure:**
343
+ ```javascript
344
+ // User ACL from URAC
345
+ {
346
+ "servicename": {
347
+ "1": { // Version
348
+ "access": true,
349
+ "get": {
350
+ "apis": { "/endpoint": { "access": true } },
351
+ "apisRegExp": [...],
352
+ "apisPermission": "restricted"
353
+ },
354
+ "post": { ... }
355
+ }
356
+ }
357
+ }
358
+ ```
359
+
360
+ **Version Resolution:**
361
+ - If request doesn't specify version, finds latest version in user's ACL
362
+ - Uses `coreLibs.version.getLatest()` for semantic version comparison
363
+
364
+ ---
365
+
366
+ ### 8. serviceCheck
367
+
368
+ **Purpose:** Validates that the tenant/user has access to the requested service.
369
+
370
+ **Location:** `mw/mt/utils.js:289`
371
+
372
+ **Behavior:**
373
+ - Skipped if `obj.skipACL = true` (from ipWhitelist)
374
+ - Checks if `obj.finalAcl` exists for the service
375
+ - If no ACL found → Error 154 (service not accessible)
376
+
377
+ **Error Codes:**
378
+ | Code | Description |
379
+ |------|-------------|
380
+ | 154 | Service not accessible |
381
+
382
+ ---
383
+
384
+ ### 9. apiCheck
385
+
386
+ **Purpose:** Validates access to the specific API endpoint based on ACL configuration.
387
+
388
+ **Location:** `mw/mt/utils.js:748`
389
+
390
+ **Behavior:**
391
+ - Skipped if `obj.skipACL = true` (from ipWhitelist)
392
+ - Matches API path against `apis` (exact match) or `apisRegExp` (pattern match)
393
+ - Sets `isAPIPublic = true` for traffic middleware throttling strategy
394
+
395
+ **API Matching Order:**
396
+ 1. Exact match in `system.apis[path]`
397
+ 2. RegExp match in `system.apisRegExp[]`
398
+
399
+ **Access Control Logic:**
400
+
401
+ | ACL Configuration | User State | Result |
402
+ |-------------------|------------|--------|
403
+ | `access: false` | Any | Public API, allowed |
404
+ | `access: true`, no user | - | Error 158 |
405
+ | `access: true`, user logged in | - | Check group permissions |
406
+ | `access: ["group1", "group2"]` | User in group | Allowed |
407
+ | `access: ["group1"]` | User not in group | Error 157/160 |
408
+ | `apisPermission: "restricted"` | API not in list | Error 159 |
409
+
410
+ **Error Codes:**
411
+ | Code | Description |
412
+ |------|-------------|
413
+ | 157 | User not in required access group |
414
+ | 158 | Login required |
415
+ | 159 | API not found (restricted mode) |
416
+ | 160 | User lacks required group membership |
417
+ | 161 | API requires login but user not authenticated |
418
+
419
+ ---
420
+
421
+ ## Waterfall Execution Flow
422
+
423
+ ```
424
+ ┌─────────────────┐
425
+ │ ipWhitelist │──▶ Sets skipACL, skipOAUTH flags
426
+ └────────┬────────┘
427
+ │
428
+ ▼
429
+ ┌─────────────────┐
430
+ │ securityGeoCheck│──▶ Error 155 if geo-blocked
431
+ └────────┬────────┘ (skipped if skipACL)
432
+ │
433
+ ▼
434
+ ┌─────────────────┐
435
+ │securityDeviceChk│──▶ Error 156 if device blocked
436
+ └────────┬────────┘ (skipped if skipACL)
437
+ │
438
+ ▼
439
+ ┌─────────────────┐
440
+ │ aclCheck │──▶ Loads finalAcl from keyACL middleware
441
+ └────────┬────────┘
442
+ │
443
+ ▼
444
+ ┌─────────────────┐
445
+ │ oauthCheck │──▶ Error 145 if PIN required
446
+ └────────┬────────┘ (skipped if skipOAUTH or public API)
447
+ │
448
+ ▼
449
+ ┌─────────────────┐
450
+ │ uracCheck │──▶ Error 146/170 if user load fails
451
+ └────────┬────────┘ (skipped if skipOAUTH)
452
+ │
453
+ ▼
454
+ ┌─────────────────┐
455
+ │ aclUrackCheck │──▶ Overrides ACL with user ACL
456
+ └────────┬────────┘ (skipped if skipOAUTH)
457
+ │
458
+ ▼
459
+ ┌─────────────────┐
460
+ │ serviceCheck │──▶ Error 154 if no service ACL
461
+ └────────┬────────┘ (skipped if skipACL)
462
+ │
463
+ ▼
464
+ ┌─────────────────┐
465
+ │ apiCheck │──▶ Error 157-161 if access denied
466
+ └────────┬────────┘ (skipped if skipACL)
467
+ │
468
+ ▼
469
+ Success
470
+ ```
471
+
472
+ ## Injection Object Structure
473
+
474
+ The `soajsinjectobj` header contains:
475
+
476
+ ```javascript
477
+ {
478
+ "tenant": {
479
+ "id": "tenant_id",
480
+ "name": "Tenant Name",
481
+ "code": "TCODE",
482
+ "locked": false,
483
+ "roaming": null,
484
+ "type": "product",
485
+ "main": { ... }, // If client tenant
486
+ "profile": { ... } // If tenant_Profile enabled
487
+ },
488
+ "key": {
489
+ "config": { ... },
490
+ "iKey": "internal_key",
491
+ "eKey": "external_key"
492
+ },
493
+ "application": {
494
+ "product": "PRODUCT",
495
+ "package": "PACKAGE",
496
+ "appId": "app_id",
497
+ "acl": { ... }, // If provision_ACL enabled
498
+ "acl_all_env": { ... }
499
+ },
500
+ "package": {
501
+ "acl": { ... }, // If provision_ACL enabled
502
+ "acl_all_env": { ... }
503
+ },
504
+ "device": { ... },
505
+ "geo": { ... },
506
+ "awareness": {
507
+ "host": "controller_host",
508
+ "port": 4000,
509
+ "interConnect": [...] // If configured
510
+ },
511
+ "urac": { // If authenticated
512
+ "_id": "user_id",
513
+ "username": "user",
514
+ "firstName": "John",
515
+ "lastName": "Doe",
516
+ "email": "user@example.com",
517
+ "groups": ["group1"],
518
+ "tenant": { "id": "...", "code": "..." },
519
+ "profile": { ... }, // If urac_Profile enabled
520
+ "acl": { ... }, // If urac_ACL enabled
521
+ "config": { ... }, // If urac_Config enabled
522
+ "groupsConfig": { ... } // If urac_GroupConfig enabled
523
+ },
524
+ "param": {
525
+ "urac_Profile": true,
526
+ "urac_ACL": true
527
+ }
528
+ }
529
+ ```
530
+
531
+ ## Service Parameters
532
+
533
+ Service-level configuration that affects MT processing:
534
+
535
+ | Parameter | Description |
536
+ |-----------|-------------|
537
+ | `urac` | Include URAC user info |
538
+ | `urac_Profile` | Include user profile |
539
+ | `urac_ACL` | Include user ACL |
540
+ | `urac_Config` | Include user config |
541
+ | `urac_GroupConfig` | Include group config |
542
+ | `tenant_Profile` | Include tenant profile |
543
+ | `provision_ACL` | Include full ACL data |
544
+ | `extKeyRequired` | Require external key |
545
+ | `oauth` | Require OAuth token |
546
+ | `interConnect` | Service-to-service discovery |
547
+
548
+ ## Error Codes
549
+
550
+ | Code | Description |
551
+ |------|-------------|
552
+ | 133 | Service version not found |
553
+ | 135 | Inject header too large |
554
+ | 152 | Package not loaded |
555
+ | 153 | Key not loaded |
556
+ | 158 | Login required for ACL route |
557
+
558
+ ## Related Files
559
+
560
+ - `mw/mt/utils.js` - Security check implementations
561
+ - `mw/mt/urac.js` - URAC driver integration
562
+
563
+ ## Usage Notes
564
+
565
+ - Proxy routes bypass all MT checks except OAuth
566
+ - Header size limited by `SOAJS_MAX_INJECT_HEADER_SIZE` (default: 64KB)
567
+ - InterConnect enables service-to-service awareness info
568
+ - URAC integration loads user profile asynchronously