@toa.io/extensions.exposition 1.0.0-alpha.278 → 1.0.0-alpha.282
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/components/identity.basic/events/principal.js +5 -1
- package/components/identity.basic/manifest.toa.yaml +11 -1
- package/components/identity.basic/operations/authenticate.js +9 -5
- package/components/identity.basic/operations/authenticate.js.map +1 -1
- package/components/identity.basic/operations/check.js +2 -1
- package/components/identity.basic/operations/check.js.map +1 -1
- package/components/identity.basic/operations/incept.js +4 -5
- package/components/identity.basic/operations/incept.js.map +1 -1
- package/components/identity.basic/operations/lib/credentials.d.ts +5 -0
- package/components/identity.basic/operations/lib/credentials.js +12 -0
- package/components/identity.basic/operations/lib/credentials.js.map +1 -0
- package/components/identity.basic/operations/transit.d.ts +2 -0
- package/components/identity.basic/operations/transit.js +12 -2
- package/components/identity.basic/operations/transit.js.map +1 -1
- package/components/identity.basic/operations/types.d.ts +11 -1
- package/components/identity.basic/source/authenticate.ts +11 -5
- package/components/identity.basic/source/check.ts +2 -1
- package/components/identity.basic/source/incept.ts +5 -5
- package/components/identity.basic/source/lib/credentials.test.ts +18 -0
- package/components/identity.basic/source/lib/credentials.ts +13 -0
- package/components/identity.basic/source/transit.ts +16 -4
- package/components/identity.basic/source/types.ts +13 -1
- package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/events/principal.js +1 -0
- package/components/identity.federation/manifest.toa.yaml +5 -1
- package/components/identity.federation/operations/authenticate.js +3 -3
- package/components/identity.federation/operations/authenticate.js.map +1 -1
- package/components/identity.federation/operations/create.js +5 -1
- package/components/identity.federation/operations/create.js.map +1 -1
- package/components/identity.federation/operations/lib/decode.js +5 -1
- package/components/identity.federation/operations/lib/decode.js.map +1 -1
- package/components/identity.federation/operations/lib/exchange.js +3 -3
- package/components/identity.federation/operations/lib/exchange.js.map +1 -1
- package/components/identity.federation/operations/list.js +2 -1
- package/components/identity.federation/operations/list.js.map +1 -1
- package/components/identity.federation/operations/types/configuration.d.ts +2 -1
- package/components/identity.federation/source/authenticate.ts +3 -3
- package/components/identity.federation/source/create.ts +5 -1
- package/components/identity.federation/source/lib/decode.ts +5 -1
- package/components/identity.federation/source/lib/exchange.ts +3 -3
- package/components/identity.federation/source/list.test.ts +1 -1
- package/components/identity.federation/source/list.ts +2 -1
- package/components/identity.federation/source/types/configuration.ts +2 -1
- package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
- package/components/identity.keys/manifest.toa.yaml +18 -1
- package/components/identity.keys/operations/disable.d.ts +5 -0
- package/components/identity.keys/operations/disable.js +5 -0
- package/components/identity.keys/operations/disable.js.map +1 -0
- package/components/identity.keys/operations/revoke.d.ts +15 -0
- package/components/identity.keys/operations/revoke.js +12 -0
- package/components/identity.keys/operations/revoke.js.map +1 -0
- package/components/identity.keys/receivers/identity.bans.created.js +1 -0
- package/components/identity.keys/receivers/identity.bans.updated.js +1 -0
- package/components/identity.keys/receivers/identity.roles.deleted.js +1 -0
- package/components/identity.keys/source/disable.ts +9 -0
- package/components/identity.keys/source/revoke.ts +30 -0
- package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/manifest.toa.yaml +6 -0
- package/components/identity.otp/operations/authenticate.js +13 -1
- package/components/identity.otp/operations/authenticate.js.map +1 -1
- package/components/identity.otp/operations/issue.js +3 -2
- package/components/identity.otp/operations/issue.js.map +1 -1
- package/components/identity.otp/operations/lib/Context.d.ts +1 -0
- package/components/identity.otp/source/authenticate.ts +19 -1
- package/components/identity.otp/source/issue.ts +3 -2
- package/components/identity.otp/source/lib/Context.ts +1 -0
- package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/operations/authenticate.js +2 -1
- package/components/identity.passkeys/operations/authenticate.js.map +1 -1
- package/components/identity.passkeys/operations/challenge.js +3 -3
- package/components/identity.passkeys/operations/challenge.js.map +1 -1
- package/components/identity.passkeys/operations/delete.js +2 -1
- package/components/identity.passkeys/operations/delete.js.map +1 -1
- package/components/identity.passkeys/operations/list.js +2 -1
- package/components/identity.passkeys/operations/list.js.map +1 -1
- package/components/identity.passkeys/source/authenticate.ts +2 -1
- package/components/identity.passkeys/source/challenge.ts +3 -3
- package/components/identity.passkeys/source/delete.ts +2 -1
- package/components/identity.passkeys/source/list.ts +2 -1
- package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.roles/manifest.toa.yaml +4 -0
- package/components/identity.roles/operations/grant.js +5 -2
- package/components/identity.roles/operations/grant.js.map +1 -1
- package/components/identity.roles/source/grant.ts +6 -2
- package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/manifest.toa.yaml +3 -0
- package/components/identity.tokens/operations/authenticate.js +4 -3
- package/components/identity.tokens/operations/authenticate.js.map +1 -1
- package/components/identity.tokens/operations/decrypt.js +7 -1
- package/components/identity.tokens/operations/decrypt.js.map +1 -1
- package/components/identity.tokens/operations/encrypt.js +3 -2
- package/components/identity.tokens/operations/encrypt.js.map +1 -1
- package/components/identity.tokens/operations/issue.js +2 -1
- package/components/identity.tokens/operations/issue.js.map +1 -1
- package/components/identity.tokens/operations/lib/types.d.ts +3 -0
- package/components/identity.tokens/source/authenticate.test.ts +26 -2
- package/components/identity.tokens/source/authenticate.ts +4 -3
- package/components/identity.tokens/source/decrypt.test.ts +35 -1
- package/components/identity.tokens/source/decrypt.ts +10 -1
- package/components/identity.tokens/source/encrypt.test.ts +15 -0
- package/components/identity.tokens/source/encrypt.ts +4 -2
- package/components/identity.tokens/source/issue.ts +2 -1
- package/components/identity.tokens/source/lib/types.ts +4 -0
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/documentation/authorities.md +3 -0
- package/documentation/components.md +31 -11
- package/documentation/identity.md +38 -6
- package/documentation/io.md +2 -2
- package/documentation/ip.md +24 -0
- package/documentation/passkeys.md +6 -0
- package/features/auth.assert.feature +2 -4
- package/features/auth.claims.feature +1 -0
- package/features/auth.incept.feature +46 -0
- package/features/authorities.feature +39 -0
- package/features/authorities.federation.feature +1 -0
- package/features/identity.bans.feature +63 -0
- package/features/identity.basic.feature +95 -3
- package/features/identity.feature +151 -0
- package/features/identity.federation.feature +5 -0
- package/features/identity.otp.feature +41 -0
- package/features/identity.roles.feature +252 -0
- package/features/identity.tokens.custom.feature +145 -0
- package/features/map.feature +2 -0
- package/features/steps/Gateway.ts +8 -2
- package/features/steps/Realtime.ts +1 -1
- package/package.json +5 -5
- package/readme.md +3 -0
- package/schemas/annotation.cos.yaml +16 -0
- package/source/Annotation.ts +14 -0
- package/source/Composition.ts +5 -5
- package/source/Directive.test.ts +3 -1
- package/source/Directive.ts +6 -2
- package/source/Endpoint.ts +4 -2
- package/source/Factory.ts +12 -9
- package/source/HTTP/Context.ts +12 -2
- package/source/HTTP/Server.ts +16 -3
- package/source/HTTP/address.test.ts +22 -0
- package/source/HTTP/address.ts +17 -0
- package/source/Query.test.ts +15 -1
- package/source/Query.ts +3 -1
- package/source/RTD/Directives.ts +10 -1
- package/source/Remotes.test.ts +7 -12
- package/source/Remotes.ts +5 -5
- package/source/const.ts +3 -0
- package/source/deployment.ts +7 -1
- package/source/directives/auth/Assert.ts +3 -0
- package/source/directives/auth/Authorization.test.ts +24 -0
- package/source/directives/auth/Authorization.ts +80 -11
- package/source/directives/auth/Incept.ts +9 -4
- package/source/directives/auth/Role.test.ts +1 -1
- package/source/directives/auth/Role.ts +2 -1
- package/source/directives/auth/schemes.test.ts +14 -0
- package/source/directives/auth/schemes.ts +14 -0
- package/source/directives/auth/types.ts +3 -0
- package/source/directives/io/IO.ts +10 -3
- package/source/directives/io/lib/throttle/Keys.ts +10 -3
- package/source/directives/io/lib/throttle/Quotas.test.ts +14 -4
- package/source/directives/io/lib/throttle/Quotas.ts +15 -2
- package/source/directives/io/lib/throttle/Sync.test.ts +7 -9
- package/source/directives/io/lib/throttle/Sync.ts +4 -7
- package/source/directives/io/lib/throttle/Warning.test.ts +32 -0
- package/source/directives/io/lib/throttle/Warning.ts +30 -0
- package/source/directives/io/lib/throttle/components/Component.ts +2 -1
- package/source/directives/io/lib/throttle/components/IP.ts +19 -28
- package/source/directives/io/lib/throttle/index.ts +1 -0
- package/source/redact.test.ts +27 -0
- package/source/redact.ts +21 -0
- package/source/schemas.test.ts +17 -0
- package/transpiled/Annotation.d.ts +10 -0
- package/transpiled/Composition.d.ts +3 -3
- package/transpiled/Composition.js +4 -4
- package/transpiled/Composition.js.map +1 -1
- package/transpiled/Directive.d.ts +3 -2
- package/transpiled/Directive.js +3 -1
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Endpoint.js +3 -2
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Factory.d.ts +3 -3
- package/transpiled/Factory.js +11 -8
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +4 -0
- package/transpiled/HTTP/Context.js +7 -2
- package/transpiled/HTTP/Context.js.map +1 -1
- package/transpiled/HTTP/Server.d.ts +5 -0
- package/transpiled/HTTP/Server.js +8 -3
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/HTTP/address.d.ts +7 -0
- package/transpiled/HTTP/address.js +14 -0
- package/transpiled/HTTP/address.js.map +1 -0
- package/transpiled/Query.js +3 -1
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RTD/Directives.d.ts +9 -1
- package/transpiled/Remotes.d.ts +3 -3
- package/transpiled/Remotes.js +4 -4
- package/transpiled/Remotes.js.map +1 -1
- package/transpiled/const.d.ts +2 -0
- package/transpiled/const.js +2 -0
- package/transpiled/const.js.map +1 -1
- package/transpiled/deployment.js +4 -0
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/directives/auth/Assert.js +2 -0
- package/transpiled/directives/auth/Assert.js.map +1 -1
- package/transpiled/directives/auth/Authorization.d.ts +8 -0
- package/transpiled/directives/auth/Authorization.js +61 -11
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/Incept.d.ts +3 -1
- package/transpiled/directives/auth/Incept.js +8 -4
- package/transpiled/directives/auth/Incept.js.map +1 -1
- package/transpiled/directives/auth/Role.js +2 -1
- package/transpiled/directives/auth/Role.js.map +1 -1
- package/transpiled/directives/auth/schemes.d.ts +10 -0
- package/transpiled/directives/auth/schemes.js +12 -0
- package/transpiled/directives/auth/schemes.js.map +1 -1
- package/transpiled/directives/auth/types.d.ts +2 -0
- package/transpiled/directives/io/IO.d.ts +4 -1
- package/transpiled/directives/io/IO.js +7 -3
- package/transpiled/directives/io/IO.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Keys.d.ts +2 -1
- package/transpiled/directives/io/lib/throttle/Keys.js +7 -2
- package/transpiled/directives/io/lib/throttle/Keys.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Quotas.d.ts +3 -2
- package/transpiled/directives/io/lib/throttle/Quotas.js +8 -1
- package/transpiled/directives/io/lib/throttle/Quotas.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Sync.d.ts +2 -3
- package/transpiled/directives/io/lib/throttle/Sync.js +1 -3
- package/transpiled/directives/io/lib/throttle/Sync.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Warning.d.ts +11 -0
- package/transpiled/directives/io/lib/throttle/Warning.js +24 -0
- package/transpiled/directives/io/lib/throttle/Warning.js.map +1 -0
- package/transpiled/directives/io/lib/throttle/components/Component.d.ts +2 -1
- package/transpiled/directives/io/lib/throttle/components/IP.d.ts +10 -2
- package/transpiled/directives/io/lib/throttle/components/IP.js +18 -23
- package/transpiled/directives/io/lib/throttle/components/IP.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/index.d.ts +1 -0
- package/transpiled/directives/io/lib/throttle/index.js +1 -0
- package/transpiled/directives/io/lib/throttle/index.js.map +1 -1
- package/transpiled/redact.d.ts +5 -0
- package/transpiled/redact.js +17 -0
- package/transpiled/redact.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/components/exposition.atom/manifest.toa.yaml +0 -21
- package/components/exposition.atom/operations/meter.js +0 -5
|
@@ -99,3 +99,44 @@ Feature: OTP authentication
|
|
|
99
99
|
|
|
100
100
|
id: ${{ bob.id }}
|
|
101
101
|
"""
|
|
102
|
+
|
|
103
|
+
Scenario: Guessing OTP codes
|
|
104
|
+
Given the `identity.otp` configuration:
|
|
105
|
+
"""yaml
|
|
106
|
+
lifetime: 60
|
|
107
|
+
attempts: 2
|
|
108
|
+
"""
|
|
109
|
+
And the Gateway is running
|
|
110
|
+
And OTP for `alice` in `nex` authority is issued
|
|
111
|
+
# alice:wrong
|
|
112
|
+
When the following request is received:
|
|
113
|
+
"""
|
|
114
|
+
GET /identity/ HTTP/1.1
|
|
115
|
+
host: nex.toa.io
|
|
116
|
+
authorization: OTP YWxpY2U6d3Jvbmc=
|
|
117
|
+
"""
|
|
118
|
+
Then the following reply is sent:
|
|
119
|
+
"""
|
|
120
|
+
401 Unauthorized
|
|
121
|
+
"""
|
|
122
|
+
When the following request is received:
|
|
123
|
+
"""
|
|
124
|
+
GET /identity/ HTTP/1.1
|
|
125
|
+
host: nex.toa.io
|
|
126
|
+
authorization: OTP YWxpY2U6d3Jvbmc=
|
|
127
|
+
"""
|
|
128
|
+
Then the following reply is sent:
|
|
129
|
+
"""
|
|
130
|
+
401 Unauthorized
|
|
131
|
+
"""
|
|
132
|
+
# the attempts are spent, the code itself is refused
|
|
133
|
+
When the following request is received:
|
|
134
|
+
"""
|
|
135
|
+
GET /identity/ HTTP/1.1
|
|
136
|
+
host: nex.toa.io
|
|
137
|
+
authorization: OTP ${{ alice.otp }}
|
|
138
|
+
"""
|
|
139
|
+
Then the following reply is sent:
|
|
140
|
+
"""
|
|
141
|
+
401 Unauthorized
|
|
142
|
+
"""
|
|
@@ -314,3 +314,255 @@ Feature: Roles management
|
|
|
314
314
|
"""
|
|
315
315
|
500 Internal Server Error
|
|
316
316
|
"""
|
|
317
|
+
|
|
318
|
+
Scenario: Role changes reach a Token holder at refresh
|
|
319
|
+
# root:secret
|
|
320
|
+
# user:pass
|
|
321
|
+
Given the `identity.basic` database contains:
|
|
322
|
+
| _id | authority | username | password |
|
|
323
|
+
| 72cf9b0ab0ac4ab2b8036e4e940ddcae | nex | root | $2b$10$Qq/qnyyU5wjrbDXyWok14OnqAZv/z.pLhz.UddatjI6eHU/rFof4i |
|
|
324
|
+
| 4344518184ad44228baffce7a44fd0b1 | nex | user | $2b$10$JoiAQUS7tzobDAFIDBWhWeEIJv933dQetyjRzSmfQGaJE5ZlJbmYy |
|
|
325
|
+
And the `identity.roles` database contains:
|
|
326
|
+
| _id | identity | role |
|
|
327
|
+
| 9c4702490ff84f2a9e1b1da2ab64bdd4 | 72cf9b0ab0ac4ab2b8036e4e940ddcae | system:identity:roles |
|
|
328
|
+
And the `identity.tokens` configuration:
|
|
329
|
+
"""yaml
|
|
330
|
+
refresh: 1
|
|
331
|
+
"""
|
|
332
|
+
And the annotation:
|
|
333
|
+
"""yaml
|
|
334
|
+
/:
|
|
335
|
+
io:output: true
|
|
336
|
+
auth:role: foo:bar
|
|
337
|
+
GET:
|
|
338
|
+
dev:stub:
|
|
339
|
+
access: granted!
|
|
340
|
+
"""
|
|
341
|
+
When the following request is received:
|
|
342
|
+
"""
|
|
343
|
+
GET /identity/ HTTP/1.1
|
|
344
|
+
host: nex.toa.io
|
|
345
|
+
authorization: Basic dXNlcjpwYXNz
|
|
346
|
+
"""
|
|
347
|
+
Then the following reply is sent:
|
|
348
|
+
"""
|
|
349
|
+
200 OK
|
|
350
|
+
authorization: Token ${{ token }}
|
|
351
|
+
"""
|
|
352
|
+
When the following request is received:
|
|
353
|
+
# root grants a role to the user
|
|
354
|
+
"""
|
|
355
|
+
POST /identity/roles/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
|
|
356
|
+
host: nex.toa.io
|
|
357
|
+
authorization: Basic cm9vdDpzZWNyZXQ=
|
|
358
|
+
content-type: application/yaml
|
|
359
|
+
|
|
360
|
+
role: foo:bar
|
|
361
|
+
"""
|
|
362
|
+
Then the following reply is sent:
|
|
363
|
+
"""
|
|
364
|
+
201 Created
|
|
365
|
+
"""
|
|
366
|
+
# the token was issued before the grant
|
|
367
|
+
When the following request is received:
|
|
368
|
+
"""
|
|
369
|
+
GET / HTTP/1.1
|
|
370
|
+
host: nex.toa.io
|
|
371
|
+
authorization: Token ${{ token }}
|
|
372
|
+
"""
|
|
373
|
+
Then the following reply is sent:
|
|
374
|
+
"""
|
|
375
|
+
403 Forbidden
|
|
376
|
+
"""
|
|
377
|
+
Then after 1 second
|
|
378
|
+
When the following request is received:
|
|
379
|
+
"""
|
|
380
|
+
GET /identity/ HTTP/1.1
|
|
381
|
+
host: nex.toa.io
|
|
382
|
+
authorization: Token ${{ token }}
|
|
383
|
+
"""
|
|
384
|
+
Then the following reply is sent:
|
|
385
|
+
"""
|
|
386
|
+
200 OK
|
|
387
|
+
authorization: Token ${{ granted }}
|
|
388
|
+
"""
|
|
389
|
+
When the following request is received:
|
|
390
|
+
"""
|
|
391
|
+
GET / HTTP/1.1
|
|
392
|
+
host: nex.toa.io
|
|
393
|
+
authorization: Token ${{ granted }}
|
|
394
|
+
accept: application/yaml
|
|
395
|
+
"""
|
|
396
|
+
Then the following reply is sent:
|
|
397
|
+
"""
|
|
398
|
+
200 OK
|
|
399
|
+
|
|
400
|
+
access: granted!
|
|
401
|
+
"""
|
|
402
|
+
When the following request is received:
|
|
403
|
+
# root revokes the role
|
|
404
|
+
"""
|
|
405
|
+
DELETE /identity/roles/4344518184ad44228baffce7a44fd0b1/foo:bar/ HTTP/1.1
|
|
406
|
+
host: nex.toa.io
|
|
407
|
+
authorization: Basic cm9vdDpzZWNyZXQ=
|
|
408
|
+
"""
|
|
409
|
+
Then the following reply is sent:
|
|
410
|
+
"""
|
|
411
|
+
200 OK
|
|
412
|
+
"""
|
|
413
|
+
Then after 1 second
|
|
414
|
+
When the following request is received:
|
|
415
|
+
"""
|
|
416
|
+
GET /identity/ HTTP/1.1
|
|
417
|
+
host: nex.toa.io
|
|
418
|
+
authorization: Token ${{ granted }}
|
|
419
|
+
"""
|
|
420
|
+
Then the following reply is sent:
|
|
421
|
+
"""
|
|
422
|
+
200 OK
|
|
423
|
+
authorization: Token ${{ revoked }}
|
|
424
|
+
"""
|
|
425
|
+
When the following request is received:
|
|
426
|
+
"""
|
|
427
|
+
GET / HTTP/1.1
|
|
428
|
+
host: nex.toa.io
|
|
429
|
+
authorization: Token ${{ revoked }}
|
|
430
|
+
"""
|
|
431
|
+
Then the following reply is sent:
|
|
432
|
+
"""
|
|
433
|
+
403 Forbidden
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
Scenario: Revoking a role without `system:identity:roles`
|
|
437
|
+
Given the `identity.basic` database contains:
|
|
438
|
+
| _id | authority | username | password |
|
|
439
|
+
| 4344518184ad44228baffce7a44fd0b1 | nex | user | $2b$10$JoiAQUS7tzobDAFIDBWhWeEIJv933dQetyjRzSmfQGaJE5ZlJbmYy |
|
|
440
|
+
And the `identity.roles` database contains:
|
|
441
|
+
| _id | identity | role |
|
|
442
|
+
| 30c969e05ff6437097ed5f07fc52358e | 4344518184ad44228baffce7a44fd0b1 | foo:bar |
|
|
443
|
+
When the following request is received:
|
|
444
|
+
"""
|
|
445
|
+
DELETE /identity/roles/4344518184ad44228baffce7a44fd0b1/foo:bar/ HTTP/1.1
|
|
446
|
+
host: nex.toa.io
|
|
447
|
+
authorization: Basic dXNlcjpwYXNz
|
|
448
|
+
"""
|
|
449
|
+
Then the following reply is sent:
|
|
450
|
+
"""
|
|
451
|
+
403 Forbidden
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
Scenario: Revoking a role revokes custom tokens
|
|
455
|
+
# root:secret
|
|
456
|
+
# user:pass
|
|
457
|
+
Given the `identity.basic` database contains:
|
|
458
|
+
| _id | authority | username | password |
|
|
459
|
+
| 72cf9b0ab0ac4ab2b8036e4e940ddcae | nex | root | $2b$10$Qq/qnyyU5wjrbDXyWok14OnqAZv/z.pLhz.UddatjI6eHU/rFof4i |
|
|
460
|
+
| 4344518184ad44228baffce7a44fd0b1 | nex | user | $2b$10$JoiAQUS7tzobDAFIDBWhWeEIJv933dQetyjRzSmfQGaJE5ZlJbmYy |
|
|
461
|
+
And the `identity.roles` database contains:
|
|
462
|
+
| _id | identity | role |
|
|
463
|
+
| 9c4702490ff84f2a9e1b1da2ab64bdd4 | 72cf9b0ab0ac4ab2b8036e4e940ddcae | system:identity:roles |
|
|
464
|
+
| 30c969e05ff6437097ed5f07fc52358e | 4344518184ad44228baffce7a44fd0b1 | foo:bar |
|
|
465
|
+
And the `identity.keys` database is empty
|
|
466
|
+
And the `identity.tokens` configuration:
|
|
467
|
+
"""yaml
|
|
468
|
+
cache:
|
|
469
|
+
ttl: 1
|
|
470
|
+
"""
|
|
471
|
+
And the annotation:
|
|
472
|
+
"""yaml
|
|
473
|
+
/:
|
|
474
|
+
io:output: true
|
|
475
|
+
auth:role: foo:bar
|
|
476
|
+
GET:
|
|
477
|
+
dev:stub:
|
|
478
|
+
access: granted!
|
|
479
|
+
"""
|
|
480
|
+
When the following request is received:
|
|
481
|
+
"""
|
|
482
|
+
POST /identity/tokens/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
|
|
483
|
+
host: nex.toa.io
|
|
484
|
+
authorization: Basic dXNlcjpwYXNz
|
|
485
|
+
accept: application/yaml
|
|
486
|
+
content-type: application/yaml
|
|
487
|
+
|
|
488
|
+
label: Forever token
|
|
489
|
+
lifetime: 0
|
|
490
|
+
"""
|
|
491
|
+
Then the following reply is sent:
|
|
492
|
+
"""
|
|
493
|
+
201 Created
|
|
494
|
+
|
|
495
|
+
token: ${{ token }}
|
|
496
|
+
"""
|
|
497
|
+
When the following request is received:
|
|
498
|
+
"""
|
|
499
|
+
GET / HTTP/1.1
|
|
500
|
+
host: nex.toa.io
|
|
501
|
+
authorization: Token ${{ token }}
|
|
502
|
+
"""
|
|
503
|
+
Then the following reply is sent:
|
|
504
|
+
"""
|
|
505
|
+
200 OK
|
|
506
|
+
"""
|
|
507
|
+
When the following request is received:
|
|
508
|
+
# root revokes the role
|
|
509
|
+
"""
|
|
510
|
+
DELETE /identity/roles/4344518184ad44228baffce7a44fd0b1/foo:bar/ HTTP/1.1
|
|
511
|
+
host: nex.toa.io
|
|
512
|
+
authorization: Basic cm9vdDpzZWNyZXQ=
|
|
513
|
+
"""
|
|
514
|
+
Then the following reply is sent:
|
|
515
|
+
"""
|
|
516
|
+
200 OK
|
|
517
|
+
"""
|
|
518
|
+
# the key is revoked eventually, and the runtime forgets it after `cache.ttl`
|
|
519
|
+
Then after 2 seconds
|
|
520
|
+
When the following request is received:
|
|
521
|
+
"""
|
|
522
|
+
GET / HTTP/1.1
|
|
523
|
+
host: nex.toa.io
|
|
524
|
+
authorization: Token ${{ token }}
|
|
525
|
+
"""
|
|
526
|
+
Then the following reply is sent:
|
|
527
|
+
"""
|
|
528
|
+
401 Unauthorized
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
Scenario: Delegating the right to delegate
|
|
532
|
+
Given the `identity.basic` database contains:
|
|
533
|
+
| _id | authority | username | password |
|
|
534
|
+
| 72cf9b0ab0ac4ab2b8036e4e940ddcae | nex | moderator | $2b$10$Qq/qnyyU5wjrbDXyWok14OnqAZv/z.pLhz.UddatjI6eHU/rFof4i |
|
|
535
|
+
| 4344518184ad44228baffce7a44fd0b1 | nex | assistant | $2b$10$JoiAQUS7tzobDAFIDBWhWeEIJv933dQetyjRzSmfQGaJE5ZlJbmYy |
|
|
536
|
+
And the `identity.roles` database contains:
|
|
537
|
+
| _id | identity | role |
|
|
538
|
+
| 9c4702490ff84f2a9e1b1da2ab64bdd4 | 72cf9b0ab0ac4ab2b8036e4e940ddcae | system:identity:roles:delegation |
|
|
539
|
+
| 30c969e05ff6437097ed5f07fc52358e | 72cf9b0ab0ac4ab2b8036e4e940ddcae | app:moderation |
|
|
540
|
+
When the following request is received:
|
|
541
|
+
"""
|
|
542
|
+
POST /identity/roles/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
|
|
543
|
+
host: nex.toa.io
|
|
544
|
+
accept: application/yaml
|
|
545
|
+
content-type: application/yaml
|
|
546
|
+
authorization: Basic bW9kZXJhdG9yOnNlY3JldA==
|
|
547
|
+
|
|
548
|
+
role: system:identity:roles:delegation
|
|
549
|
+
"""
|
|
550
|
+
Then the following reply is sent:
|
|
551
|
+
"""
|
|
552
|
+
422 Unprocessable Entity
|
|
553
|
+
|
|
554
|
+
code: INACCESSIBLE_SCOPE
|
|
555
|
+
"""
|
|
556
|
+
When the following request is received:
|
|
557
|
+
"""
|
|
558
|
+
GET /identity/roles/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
|
|
559
|
+
host: nex.toa.io
|
|
560
|
+
accept: application/yaml
|
|
561
|
+
authorization: Basic YXNzaXN0YW50OnBhc3M=
|
|
562
|
+
"""
|
|
563
|
+
Then the following reply is sent:
|
|
564
|
+
"""
|
|
565
|
+
200 OK
|
|
566
|
+
|
|
567
|
+
[]
|
|
568
|
+
"""
|
|
@@ -283,3 +283,148 @@ Feature: Custom tokens
|
|
|
283
283
|
"""
|
|
284
284
|
401 Unauthorized
|
|
285
285
|
"""
|
|
286
|
+
|
|
287
|
+
Scenario: Custom token is not refreshed
|
|
288
|
+
Given the `identity.tokens` configuration:
|
|
289
|
+
"""yaml
|
|
290
|
+
refresh: 1
|
|
291
|
+
"""
|
|
292
|
+
When the following request is received:
|
|
293
|
+
"""
|
|
294
|
+
POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
295
|
+
host: nex.toa.io
|
|
296
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
297
|
+
accept: application/yaml
|
|
298
|
+
content-type: application/yaml
|
|
299
|
+
|
|
300
|
+
label: Restricted token
|
|
301
|
+
lifetime: 600
|
|
302
|
+
permissions:
|
|
303
|
+
/notes/: [GET]
|
|
304
|
+
"""
|
|
305
|
+
Then the following reply is sent:
|
|
306
|
+
"""
|
|
307
|
+
201 Created
|
|
308
|
+
|
|
309
|
+
token: ${{ token }}
|
|
310
|
+
"""
|
|
311
|
+
And after 1 second
|
|
312
|
+
When the following request is received:
|
|
313
|
+
"""
|
|
314
|
+
GET /notes/ HTTP/1.1
|
|
315
|
+
host: nex.toa.io
|
|
316
|
+
authorization: Token ${{ token }}
|
|
317
|
+
"""
|
|
318
|
+
Then the following reply is sent:
|
|
319
|
+
"""
|
|
320
|
+
200 OK
|
|
321
|
+
"""
|
|
322
|
+
And the reply does not contain:
|
|
323
|
+
"""
|
|
324
|
+
authorization: Token
|
|
325
|
+
"""
|
|
326
|
+
When the following request is received:
|
|
327
|
+
"""
|
|
328
|
+
GET /notes/public/ HTTP/1.1
|
|
329
|
+
host: nex.toa.io
|
|
330
|
+
authorization: Token ${{ token }}
|
|
331
|
+
"""
|
|
332
|
+
Then the following reply is sent:
|
|
333
|
+
"""
|
|
334
|
+
403 Forbidden
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
Scenario: Permissions apply to the path the request is routed by
|
|
338
|
+
When the following request is received:
|
|
339
|
+
"""
|
|
340
|
+
POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
341
|
+
host: nex.toa.io
|
|
342
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
343
|
+
accept: application/yaml
|
|
344
|
+
content-type: application/yaml
|
|
345
|
+
|
|
346
|
+
label: Public notes token
|
|
347
|
+
lifetime: 0
|
|
348
|
+
permissions:
|
|
349
|
+
/notes/public/: [GET]
|
|
350
|
+
"""
|
|
351
|
+
Then the following reply is sent:
|
|
352
|
+
"""
|
|
353
|
+
201 Created
|
|
354
|
+
|
|
355
|
+
token: ${{ token }}
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
# the query string is not part of the path
|
|
359
|
+
When the following request is received:
|
|
360
|
+
"""
|
|
361
|
+
GET /notes/public/?page=2 HTTP/1.1
|
|
362
|
+
host: nex.toa.io
|
|
363
|
+
authorization: Token ${{ token }}
|
|
364
|
+
"""
|
|
365
|
+
Then the following reply is sent:
|
|
366
|
+
"""
|
|
367
|
+
200 OK
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
Scenario: Changing the password revokes custom tokens
|
|
371
|
+
Given the `identity.basic` database contains:
|
|
372
|
+
| _id | _version | authority | username | password |
|
|
373
|
+
| efe3a65ebbee47ed95a73edd911ea328 | 1 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
374
|
+
And the `identity.tokens` configuration:
|
|
375
|
+
"""yaml
|
|
376
|
+
cache:
|
|
377
|
+
ttl: 1
|
|
378
|
+
"""
|
|
379
|
+
When the following request is received:
|
|
380
|
+
"""
|
|
381
|
+
POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
382
|
+
host: nex.toa.io
|
|
383
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
384
|
+
accept: application/yaml
|
|
385
|
+
content-type: application/yaml
|
|
386
|
+
|
|
387
|
+
label: Forever token
|
|
388
|
+
lifetime: 0
|
|
389
|
+
"""
|
|
390
|
+
Then the following reply is sent:
|
|
391
|
+
"""
|
|
392
|
+
201 Created
|
|
393
|
+
|
|
394
|
+
token: ${{ token }}
|
|
395
|
+
"""
|
|
396
|
+
When the following request is received:
|
|
397
|
+
"""
|
|
398
|
+
GET /identity/ HTTP/1.1
|
|
399
|
+
host: nex.toa.io
|
|
400
|
+
authorization: Token ${{ token }}
|
|
401
|
+
"""
|
|
402
|
+
Then the following reply is sent:
|
|
403
|
+
"""
|
|
404
|
+
200 OK
|
|
405
|
+
"""
|
|
406
|
+
When the following request is received:
|
|
407
|
+
"""
|
|
408
|
+
PATCH /identity/basic/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
409
|
+
host: nex.toa.io
|
|
410
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
411
|
+
content-type: application/yaml
|
|
412
|
+
|
|
413
|
+
password: new-secret
|
|
414
|
+
"""
|
|
415
|
+
Then the following reply is sent:
|
|
416
|
+
"""
|
|
417
|
+
200 OK
|
|
418
|
+
"""
|
|
419
|
+
# the runtime forgets the key after `cache.ttl`
|
|
420
|
+
Then after 1.5 seconds
|
|
421
|
+
When the following request is received:
|
|
422
|
+
"""
|
|
423
|
+
GET /identity/ HTTP/1.1
|
|
424
|
+
host: nex.toa.io
|
|
425
|
+
authorization: Token ${{ token }}
|
|
426
|
+
"""
|
|
427
|
+
Then the following reply is sent:
|
|
428
|
+
"""
|
|
429
|
+
401 Unauthorized
|
|
430
|
+
"""
|
package/features/map.feature
CHANGED
|
@@ -235,6 +235,7 @@ Feature: HTTP context mapping
|
|
|
235
235
|
"""yaml
|
|
236
236
|
trust:
|
|
237
237
|
- iss: http://localhost:44444
|
|
238
|
+
aud: test
|
|
238
239
|
"""
|
|
239
240
|
And the `echo` is running with the following manifest:
|
|
240
241
|
"""yaml
|
|
@@ -269,6 +270,7 @@ Feature: HTTP context mapping
|
|
|
269
270
|
"""yaml
|
|
270
271
|
trust:
|
|
271
272
|
- iss: http://localhost:44444
|
|
273
|
+
aud: test
|
|
272
274
|
assert: false
|
|
273
275
|
"""
|
|
274
276
|
And the `pots` is running with the following manifest:
|
|
@@ -29,12 +29,18 @@ export class Gateway {
|
|
|
29
29
|
process.env.TOA_EXPOSITION = JSON.stringify(tree)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const { debug, authorities } = annotation
|
|
32
|
+
const { debug, authorities, bouncer, ip } = annotation
|
|
33
33
|
const properties = Object.assign({}, DEFAULT_PROPERTIES)
|
|
34
34
|
|
|
35
35
|
if (debug !== undefined)
|
|
36
36
|
properties.debug = debug
|
|
37
37
|
|
|
38
|
+
if (bouncer !== undefined)
|
|
39
|
+
properties.bouncer = bouncer
|
|
40
|
+
|
|
41
|
+
if (ip !== undefined)
|
|
42
|
+
properties.ip = ip
|
|
43
|
+
|
|
38
44
|
if (authorities !== undefined)
|
|
39
45
|
properties.authorities = authorities
|
|
40
46
|
|
|
@@ -130,7 +136,7 @@ export class Gateway {
|
|
|
130
136
|
|
|
131
137
|
this.writeConfiguration()
|
|
132
138
|
|
|
133
|
-
const factory = new Factory(boot)
|
|
139
|
+
const factory = new Factory(boot.host())
|
|
134
140
|
const service = await factory.service()
|
|
135
141
|
|
|
136
142
|
if (service === null)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/extensions.exposition",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.282",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa Exposition",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@simplewebauthn/server": "14.0.0",
|
|
22
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
23
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
24
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
22
|
+
"@toa.io/core": "1.0.0-alpha.282",
|
|
23
|
+
"@toa.io/generic": "1.0.0-alpha.282",
|
|
24
|
+
"@toa.io/schemas": "1.0.0-alpha.282",
|
|
25
25
|
"bcryptjs": "3.0.3",
|
|
26
26
|
"content-type": "3.0.0",
|
|
27
27
|
"jose": "6.2.10",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"@types/cors": "2.8.19",
|
|
57
57
|
"@types/negotiator": "0.6.5"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "4007c814725dd015d3bbebb9a28e28a247645c4f"
|
|
60
60
|
}
|
package/readme.md
CHANGED
|
@@ -136,6 +136,8 @@ exposition:
|
|
|
136
136
|
| `class` | Ingress class. |
|
|
137
137
|
| `annotations` | Ingress annotations. |
|
|
138
138
|
| `debug` | Output server errors. Default `false`. |
|
|
139
|
+
| `ip` | Header the client address is read from. See [Client address](documentation/ip.md). |
|
|
140
|
+
| `bouncer` | Meter of failed authentications per address. See [Identity authentication](documentation/identity.md#failed-authentications). |
|
|
139
141
|
|
|
140
142
|
### Observability
|
|
141
143
|
|
|
@@ -201,6 +203,7 @@ See also [telemetry ready probe](../telemetry/readme.md#ready-probe) and
|
|
|
201
203
|
## See Also
|
|
202
204
|
|
|
203
205
|
- [Protocol support](documentation/protocol.md)
|
|
206
|
+
- [Client address](documentation/ip.md)
|
|
204
207
|
- [Resource Tree Definition](documentation/tree.md)
|
|
205
208
|
- [Identity authentication](documentation/identity.md)
|
|
206
209
|
- [Access authorization](documentation/access.md)
|
|
@@ -34,6 +34,19 @@ properties:
|
|
|
34
34
|
type: string
|
|
35
35
|
debug:
|
|
36
36
|
type: boolean
|
|
37
|
+
ip:
|
|
38
|
+
type: string
|
|
39
|
+
minLength: 1
|
|
40
|
+
bouncer:
|
|
41
|
+
type: object
|
|
42
|
+
properties:
|
|
43
|
+
attempts:
|
|
44
|
+
type: integer
|
|
45
|
+
minimum: 1
|
|
46
|
+
interval:
|
|
47
|
+
type: integer
|
|
48
|
+
minimum: 1
|
|
49
|
+
additionalProperties: false
|
|
37
50
|
protocol:
|
|
38
51
|
type: string
|
|
39
52
|
enum: [h1, h2c]
|
|
@@ -46,3 +59,6 @@ properties:
|
|
|
46
59
|
type: object
|
|
47
60
|
required:
|
|
48
61
|
- authorities
|
|
62
|
+
dependencies:
|
|
63
|
+
bouncer:
|
|
64
|
+
- ip
|
package/source/Annotation.ts
CHANGED
|
@@ -17,5 +17,19 @@ export interface Annotation {
|
|
|
17
17
|
service?: { annotations?: Record<string, string> }
|
|
18
18
|
|
|
19
19
|
debug?: boolean
|
|
20
|
+
|
|
21
|
+
/** The header the client address is read from; the connection's without one. See `ip.md`. */
|
|
22
|
+
ip?: string
|
|
23
|
+
|
|
24
|
+
/** Failed authentications an address may make; none are metered unless set. See `identity.md`. */
|
|
25
|
+
bouncer?: Bouncer
|
|
20
26
|
'/'?: object // parsed and validated by RTD.syntax.parse
|
|
21
27
|
}
|
|
28
|
+
|
|
29
|
+
export interface Bouncer {
|
|
30
|
+
/** what an address may fail at once, 20 by default */
|
|
31
|
+
attempts?: number
|
|
32
|
+
|
|
33
|
+
/** seconds it takes to earn them back, 60 by default */
|
|
34
|
+
interval?: number
|
|
35
|
+
}
|
package/source/Composition.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { readdirSync, type Dirent } from 'node:fs'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import { Connector } from '@toa.io/core'
|
|
4
|
-
import { type
|
|
4
|
+
import { type Host } from './Factory.js'
|
|
5
5
|
|
|
6
6
|
export class Composition extends Connector {
|
|
7
|
-
private readonly
|
|
7
|
+
private readonly host: Host
|
|
8
8
|
|
|
9
|
-
public constructor (
|
|
9
|
+
public constructor (host: Host) {
|
|
10
10
|
super()
|
|
11
|
-
this.
|
|
11
|
+
this.host = host
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
protected override async open (): Promise<void> {
|
|
15
15
|
const paths = find()
|
|
16
|
-
const composition = await this.
|
|
16
|
+
const composition = await this.host.composition(paths)
|
|
17
17
|
|
|
18
18
|
await composition.connect()
|
|
19
19
|
|
package/source/Directive.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { generate } from 'randomstring'
|
|
|
5
5
|
import { DirectivesFactory } from './Directive.js'
|
|
6
6
|
import type { syntax, DirectiveFamily } from './RTD/index.js'
|
|
7
7
|
import type { Remotes } from './Remotes.js'
|
|
8
|
+
import type { Host } from './Factory.js'
|
|
8
9
|
import type { Context } from './HTTP/index.js'
|
|
9
10
|
|
|
10
11
|
const sequence: string[] = []
|
|
@@ -51,7 +52,8 @@ beforeEach(() => {
|
|
|
51
52
|
family.preflight.mock.mockImplementation(() => { sequence.push(family.name); return null })
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
factory = new DirectivesFactory(families, {} as unknown as Remotes
|
|
55
|
+
factory = new DirectivesFactory(families, {} as unknown as Remotes, {} as unknown as Host,
|
|
56
|
+
{ authorities: {} })
|
|
55
57
|
})
|
|
56
58
|
|
|
57
59
|
it('should create directive', async () => {
|
package/source/Directive.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { console, type SpanOptions } from 'openspan'
|
|
2
|
-
import type { Context, OutgoingMessage } from './HTTP/index.js'
|
|
2
|
+
import type { Context, OutgoingMessage, Options } from './HTTP/index.js'
|
|
3
3
|
import type { Remotes } from './Remotes.js'
|
|
4
|
+
import type { Host } from './Factory.js'
|
|
4
5
|
import type { Output } from './io.js'
|
|
5
6
|
import type * as RTD from './RTD/index.js'
|
|
6
7
|
|
|
@@ -64,8 +65,11 @@ export class DirectivesFactory implements RTD.DirectiveFactory {
|
|
|
64
65
|
private readonly mandatory: string[] = []
|
|
65
66
|
private readonly instances: Directives[] = []
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
// eslint-disable-next-line max-params
|
|
69
|
+
public constructor (families: RTD.DirectiveFamily[], remotes: Remotes, host: Host,
|
|
70
|
+
options: Options) {
|
|
68
71
|
for (const family of families) {
|
|
72
|
+
family.mount?.(host, options)
|
|
69
73
|
this.families[family.name] = family
|
|
70
74
|
|
|
71
75
|
if (family.mandatory)
|
package/source/Endpoint.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
2
|
import { console } from 'openspan'
|
|
3
3
|
import { Mapping } from './Mapping.js'
|
|
4
|
+
import { redact } from './redact.js'
|
|
4
5
|
import * as http from './HTTP/index.js'
|
|
5
6
|
import type { Introspection, Schema } from './Introspection.js'
|
|
6
7
|
import type { Remote } from '@toa.io/core'
|
|
@@ -29,11 +30,12 @@ export class Endpoint implements RTD.Endpoint {
|
|
|
29
30
|
|
|
30
31
|
const endpoint = this.remote.locator.id + '.' + this.endpoint
|
|
31
32
|
|
|
32
|
-
console.debug('Calling operation', { endpoint, request })
|
|
33
|
+
console.debug('Calling operation', { endpoint, request: redact(request) })
|
|
33
34
|
|
|
34
35
|
const reply = await this.remote.invoke(this.endpoint, request)
|
|
35
36
|
|
|
36
|
-
console.debug('Received reply',
|
|
37
|
+
console.debug('Received reply',
|
|
38
|
+
{ endpoint, reply: reply instanceof Readable ? '[Readable stream]' : redact(reply) })
|
|
37
39
|
|
|
38
40
|
if (reply instanceof Error)
|
|
39
41
|
throw new http.UnprocessableEntity(reply)
|