@toa.io/extensions.exposition 1.0.0-alpha.12 → 1.0.0-alpha.14

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 (90) hide show
  1. package/components/identity.bans/manifest.toa.yaml +13 -5
  2. package/components/identity.bans/operations/transit.d.ts +14 -0
  3. package/components/identity.bans/operations/transit.js +11 -0
  4. package/components/identity.bans/operations/transit.js.map +1 -0
  5. package/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -0
  6. package/components/identity.bans/source/transit.ts +21 -0
  7. package/components/identity.bans/tsconfig.json +9 -0
  8. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  9. package/components/identity.roles/manifest.toa.yaml +4 -4
  10. package/components/identity.roles/operations/grant.d.ts +1 -1
  11. package/components/identity.roles/operations/grant.js +6 -5
  12. package/components/identity.roles/operations/grant.js.map +1 -1
  13. package/components/identity.roles/operations/lib/Entity.d.ts +1 -1
  14. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  15. package/components/identity.roles/source/grant.ts +7 -6
  16. package/components/identity.roles/source/lib/Entity.ts +1 -1
  17. package/components/identity.tokens/manifest.toa.yaml +11 -2
  18. package/components/identity.tokens/operations/authenticate.js +5 -2
  19. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  20. package/components/identity.tokens/operations/encrypt.js +4 -1
  21. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  22. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  23. package/components/identity.tokens/operations/types.d.ts +1 -2
  24. package/components/identity.tokens/receivers/identity.bans.created.js +3 -0
  25. package/components/identity.tokens/source/authenticate.ts +5 -2
  26. package/components/identity.tokens/source/encrypt.test.ts +22 -1
  27. package/components/identity.tokens/source/encrypt.ts +4 -1
  28. package/components/identity.tokens/source/types.ts +1 -2
  29. package/documentation/access.md +13 -3
  30. package/documentation/components.md +17 -3
  31. package/features/access.feature +16 -48
  32. package/features/errors.feature +3 -1
  33. package/features/identity.bans.feature +128 -0
  34. package/features/identity.basic.feature +4 -7
  35. package/features/identity.federation.feature +1 -0
  36. package/features/identity.roles.feature +54 -1
  37. package/features/identity.tokens.feature +41 -0
  38. package/features/io.feature +29 -0
  39. package/features/response.feature +4 -1
  40. package/features/steps/Database.ts +16 -9
  41. package/features/steps/IdP.ts +28 -23
  42. package/features/steps/components/echo/manifest.toa.yaml +5 -1
  43. package/features/steps/components/echo/operations/identity.js +7 -0
  44. package/features/steps/components/users/manifest.toa.yaml +0 -1
  45. package/package.json +11 -9
  46. package/source/Directive.ts +3 -1
  47. package/source/Gateway.ts +1 -1
  48. package/source/HTTP/exceptions.ts +1 -1
  49. package/source/RTD/factory.ts +1 -1
  50. package/source/Tenant.ts +0 -8
  51. package/source/directives/auth/Authorization.ts +1 -1
  52. package/source/directives/auth/Delegate.ts +4 -1
  53. package/source/directives/auth/Role.test.ts +53 -6
  54. package/source/directives/auth/Role.ts +23 -5
  55. package/source/directives/auth/types.ts +1 -1
  56. package/source/directives/cache/Cache.ts +1 -1
  57. package/source/directives/dev/Development.ts +1 -1
  58. package/source/directives/octets/Octets.ts +1 -1
  59. package/source/directives/vary/Vary.ts +1 -1
  60. package/source/exceptions.ts +13 -6
  61. package/transpiled/Directive.js +3 -1
  62. package/transpiled/Directive.js.map +1 -1
  63. package/transpiled/Gateway.js +1 -1
  64. package/transpiled/Gateway.js.map +1 -1
  65. package/transpiled/HTTP/exceptions.d.ts +1 -1
  66. package/transpiled/HTTP/exceptions.js.map +1 -1
  67. package/transpiled/RTD/factory.js +1 -1
  68. package/transpiled/RTD/factory.js.map +1 -1
  69. package/transpiled/Tenant.d.ts +0 -1
  70. package/transpiled/Tenant.js +0 -6
  71. package/transpiled/Tenant.js.map +1 -1
  72. package/transpiled/directives/auth/Authorization.js +1 -1
  73. package/transpiled/directives/auth/Authorization.js.map +1 -1
  74. package/transpiled/directives/auth/Delegate.js +3 -1
  75. package/transpiled/directives/auth/Delegate.js.map +1 -1
  76. package/transpiled/directives/auth/Role.d.ts +4 -1
  77. package/transpiled/directives/auth/Role.js +19 -5
  78. package/transpiled/directives/auth/Role.js.map +1 -1
  79. package/transpiled/directives/cache/Cache.js +1 -1
  80. package/transpiled/directives/cache/Cache.js.map +1 -1
  81. package/transpiled/directives/dev/Development.js +1 -1
  82. package/transpiled/directives/dev/Development.js.map +1 -1
  83. package/transpiled/directives/octets/Octets.js +1 -1
  84. package/transpiled/directives/octets/Octets.js.map +1 -1
  85. package/transpiled/directives/vary/Vary.js +1 -1
  86. package/transpiled/directives/vary/Vary.js.map +1 -1
  87. package/transpiled/exceptions.d.ts +3 -2
  88. package/transpiled/exceptions.js +7 -1
  89. package/transpiled/exceptions.js.map +1 -1
  90. package/transpiled/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,128 @@
1
+ @security
2
+ Feature: Bans
3
+
4
+ Background:
5
+ Given the `identity.basic` database contains:
6
+ # developer:secret
7
+ # user:12345
8
+ | _id | username | password | _deleted |
9
+ | efe3a65ebbee47ed95a73edd911ea328 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O | null |
10
+ | e8e4f9c2a68d419b861403d71fabc915 | user | $2b$10$Frszmrmsz9iwSXzBbRRMKeDVKsNxozkrLNSsN.SnVC.KPxLtQr/bK | null |
11
+ And the `identity.bans` database is empty
12
+
13
+ Scenario: Banning an Identity
14
+ Given the `identity.roles` database contains:
15
+ | _id | identity | role |
16
+ | 775a648d054e4ce1a65f8f17e5b51803 | efe3a65ebbee47ed95a73edd911ea328 | system:identity:bans |
17
+ And the annotation:
18
+ """yaml
19
+ /:
20
+ /:id:
21
+ io:output: true
22
+ auth:id: id
23
+ GET:
24
+ dev:stub:
25
+ access: granted!
26
+ """
27
+ And the `identity.tokens` configuration:
28
+ """yaml
29
+ refresh: 1
30
+ """
31
+ When the following request is received:
32
+ """
33
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
34
+ authorization: Basic dXNlcjoxMjM0NQ==
35
+ """
36
+ Then the following reply is sent:
37
+ """
38
+ 200 OK
39
+ authorization: Token ${{ token }}
40
+ """
41
+ When the following request is received:
42
+ """
43
+ PUT /identity/bans/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
44
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
45
+ content-type: application/yaml
46
+
47
+ banned: true
48
+ comment: Bye bye
49
+ """
50
+ Then the following reply is sent:
51
+ """
52
+ 200 OK
53
+ """
54
+ # accessing a resource with a banned Identity
55
+ When the following request is received:
56
+ """
57
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
58
+ authorization: Basic dXNlcjoxMjM0NQ==
59
+ """
60
+ Then the following reply is sent:
61
+ """
62
+ 401 Unauthorized
63
+ """
64
+ Then after 1 second
65
+ When the following request is received:
66
+ """
67
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
68
+ authorization: Token ${{ token }}
69
+ """
70
+ Then the following reply is sent:
71
+ """
72
+ 401 Unauthorized
73
+ """
74
+ When the following request is received:
75
+ """
76
+ PUT /identity/bans/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
77
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
78
+ content-type: application/yaml
79
+
80
+ banned: false
81
+ """
82
+ Then the following reply is sent:
83
+ """
84
+ 200 OK
85
+ """
86
+ When the following request is received:
87
+ """
88
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
89
+ authorization: Basic dXNlcjoxMjM0NQ==
90
+ """
91
+ Then the following reply is sent:
92
+ """
93
+ 200 OK
94
+
95
+ authorization: Token ${{ new_token }}
96
+ """
97
+ # re-ban
98
+ When the following request is received:
99
+ """
100
+ PUT /identity/bans/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
101
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
102
+ content-type: application/yaml
103
+
104
+ banned: true
105
+ """
106
+ Then the following reply is sent:
107
+ """
108
+ 200 OK
109
+ """
110
+ When the following request is received:
111
+ """
112
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
113
+ authorization: Basic dXNlcjoxMjM0NQ==
114
+ """
115
+ Then the following reply is sent:
116
+ """
117
+ 401 Unauthorized
118
+ """
119
+ Then after 1 second
120
+ When the following request is received:
121
+ """
122
+ GET /e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
123
+ authorization: Token ${{ new_token }}
124
+ """
125
+ Then the following reply is sent:
126
+ """
127
+ 401 Unauthorized
128
+ """
@@ -1,3 +1,4 @@
1
+ @security
1
2
  Feature: Basic authentication
2
3
 
3
4
  Background:
@@ -28,8 +29,6 @@ Feature: Basic authentication
28
29
  Then the following reply is sent:
29
30
  """
30
31
  409 Conflict
31
-
32
- - username
33
32
  """
34
33
 
35
34
  Scenario: Creating new Identity using inception
@@ -43,9 +42,9 @@ Feature: Basic authentication
43
42
  incept: id
44
43
  endpoint: transit
45
44
  query: ~
46
- /:id: # credential testing route
47
- id: id
48
- GET: observe
45
+ /:id: # credential testing route
46
+ id: id
47
+ GET: observe
49
48
  """
50
49
  When the following request is received:
51
50
  """
@@ -96,8 +95,6 @@ Feature: Basic authentication
96
95
  Then the following reply is sent:
97
96
  """
98
97
  409 Conflict
99
-
100
- - username
101
98
  """
102
99
  # credentials already exists
103
100
  When the following request is received:
@@ -1,3 +1,4 @@
1
+ @security
1
2
  Feature: Identity Federation
2
3
 
3
4
  Background:
@@ -1,6 +1,7 @@
1
+ @security
1
2
  Feature: Roles management
2
3
 
3
- Scenario: Adding a role to an Identity
4
+ Scenario: Granting a role to an Identity
4
5
  # root:secret
5
6
  # user:pass
6
7
  Given the `identity.basic` database contains:
@@ -34,6 +35,7 @@ Feature: Roles management
34
35
  """
35
36
  POST /identity/roles/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
36
37
  authorization: Basic cm9vdDpzZWNyZXQ=
38
+ accept: application/yaml
37
39
  content-type: application/yaml
38
40
 
39
41
  role: test
@@ -41,6 +43,8 @@ Feature: Roles management
41
43
  Then the following reply is sent:
42
44
  """
43
45
  201 Created
46
+
47
+ grantor: 72cf9b0ab0ac4ab2b8036e4e940ddcae
44
48
  """
45
49
  When the following request is received:
46
50
  # user now have the role
@@ -200,3 +204,52 @@ Feature: Roles management
200
204
  | app! |
201
205
  | app: |
202
206
  | app:no spaces |
207
+
208
+ Scenario: Dynamic roles
209
+ Given the `identity.basic` database contains:
210
+ | _id | username | password |
211
+ | 72cf9b0ab0ac4ab2b8036e4e940ddcae | moderator | $2b$10$Qq/qnyyU5wjrbDXyWok14OnqAZv/z.pLhz.UddatjI6eHU/rFof4i |
212
+ And the `identity.roles` database contains:
213
+ | _id | identity | role |
214
+ | 30c969e05ff6437097ed5f07fc52358e | 72cf9b0ab0ac4ab2b8036e4e940ddcae | app:29e54ae1:moderation |
215
+ And the annotation:
216
+ """yaml
217
+ /:
218
+ /broken:
219
+ auth:role: app:{org}:moderation
220
+ GET:
221
+ dev:stub: never
222
+ /:org:
223
+ io:output: true
224
+ auth:role: app:{org}:moderation
225
+ GET:
226
+ dev:stub:
227
+ access: granted!
228
+ """
229
+ When the following request is received:
230
+ """
231
+ GET /29e54ae1/ HTTP/1.1
232
+ authorization: Basic bW9kZXJhdG9yOnNlY3JldA==
233
+ """
234
+ Then the following reply is sent:
235
+ """
236
+ 200 OK
237
+ """
238
+ When the following request is received:
239
+ """
240
+ GET /88584c9b/ HTTP/1.1
241
+ authorization: Basic bW9kZXJhdG9yOnNlY3JldA==
242
+ """
243
+ Then the following reply is sent:
244
+ """
245
+ 403 Forbidden
246
+ """
247
+ When the following request is received:
248
+ """
249
+ GET /broken/ HTTP/1.1
250
+ authorization: Basic bW9kZXJhdG9yOnNlY3JldA==
251
+ """
252
+ Then the following reply is sent:
253
+ """
254
+ 500 Internal Server Error
255
+ """
@@ -1,3 +1,4 @@
1
+ @security
1
2
  Feature: Tokens lifecycle
2
3
 
3
4
  Scenario: Switching to Token authentication scheme
@@ -120,3 +121,43 @@ Feature: Tokens lifecycle
120
121
  """
121
122
  401 Unauthorized
122
123
  """
124
+
125
+ Scenario: Issuing own token
126
+ Given the `identity.basic` database contains:
127
+ | _id | username | password |
128
+ | efe3a65ebbee47ed95a73edd911ea328 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
129
+ When the following request is received:
130
+ """
131
+ GET /identity/ HTTP/1.1
132
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
133
+ """
134
+ Then the following reply is sent:
135
+ """
136
+ 200 OK
137
+ authorization: Token ${{ token }}
138
+ """
139
+ When the following request is received:
140
+ """
141
+ POST /identity/tokens/ HTTP/1.1
142
+ authorization: Token ${{ token }}
143
+ content-type: application/yaml
144
+
145
+ lifetime: 0
146
+ """
147
+ Then the following reply is sent:
148
+ """
149
+ 201 Created
150
+ """
151
+ # Token scheme must be used
152
+ When the following request is received:
153
+ """
154
+ POST /identity/tokens/ HTTP/1.1
155
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
156
+ content-type: application/yaml
157
+
158
+ lifetime: 60
159
+ """
160
+ Then the following reply is sent:
161
+ """
162
+ 403 Forbidden
163
+ """
@@ -1,3 +1,4 @@
1
+ @security
1
2
  Feature: IO restrictions
2
3
 
3
4
  Background:
@@ -135,6 +136,7 @@ Feature: IO restrictions
135
136
  exposition:
136
137
  /:
137
138
  io:input: [title, volume]
139
+ io:output: [id]
138
140
  POST: create
139
141
  """
140
142
  When the following request is received:
@@ -165,3 +167,30 @@ Feature: IO restrictions
165
167
  """
166
168
  201 Created
167
169
  """
170
+
171
+ Scenario: IO shortcuts
172
+ Given the `pots` is running with the following manifest:
173
+ """yaml
174
+ exposition:
175
+ /:
176
+ input: [title, volume]
177
+ output: [id, title, volume]
178
+ POST: create
179
+ """
180
+ When the following request is received:
181
+ """
182
+ POST /pots/ HTTP/1.1
183
+ accept: application/yaml
184
+ content-type: application/yaml
185
+
186
+ title: Hello
187
+ volume: 1.5
188
+ """
189
+ Then the following reply is sent:
190
+ """
191
+ 201 Created
192
+
193
+ id:
194
+ title: Hello
195
+ volume: 1.5
196
+ """
@@ -7,7 +7,8 @@ Feature: Response
7
7
  io:output: true
8
8
  GET:
9
9
  anonymous: true
10
- dev:stub: hello
10
+ dev:stub:
11
+ hello: world
11
12
  """
12
13
  When the following request is received:
13
14
  """
@@ -19,6 +20,8 @@ Feature: Response
19
20
  200 OK
20
21
  content-type: application/json
21
22
  vary: accept
23
+
24
+ {"hello":"world"}
22
25
  """
23
26
 
24
27
  Scenario: Error as YAML
@@ -1,5 +1,6 @@
1
1
  import { afterAll, beforeAll, binding, given } from 'cucumber-tsflow'
2
2
  import { MongoClient } from 'mongodb'
3
+ import type { Collection } from 'mongodb'
3
4
  import type { DataTable } from '@cucumber/cucumber'
4
5
 
5
6
  @binding()
@@ -8,9 +9,7 @@ export class Database {
8
9
 
9
10
  @given('the `{word}` database contains:')
10
11
  public async upsert (id: string, table: DataTable): Promise<void> {
11
- const [name, namespace = 'default'] = id.split('.').reverse()
12
- const collection = Database.client.db(namespace).collection(name)
13
-
12
+ const collection = this.collection(id)
14
13
  const columns = table.raw()[0]
15
14
  const rows = table.rows()
16
15
  const documents: Document[] = []
@@ -22,7 +21,11 @@ export class Database {
22
21
  const str = rows[r][c]
23
22
  const int = parseInt(str)
24
23
 
25
- document[columns[c]] = int.toString() === str ? int : str
24
+ document[columns[c]] = int.toString() === str
25
+ ? int
26
+ : str === 'null'
27
+ ? null
28
+ : str
26
29
  }
27
30
 
28
31
  documents.push(document)
@@ -36,10 +39,7 @@ export class Database {
36
39
 
37
40
  @given('the `{word}` database is empty')
38
41
  public async truncate (id: string): Promise<void> {
39
- const [name, namespace = 'default'] = id.split('.').reverse()
40
- const collection = Database.client.db(namespace).collection(name)
41
-
42
- await collection.deleteMany({})
42
+ await this.collection(id).deleteMany({})
43
43
  }
44
44
 
45
45
  @beforeAll()
@@ -53,6 +53,13 @@ export class Database {
53
53
  public static async disconnect (): Promise<void> {
54
54
  await this.client.close()
55
55
  }
56
+
57
+ private collection (id: string): Collection {
58
+ const [name, namespace = 'default'] = id.split('.').reverse()
59
+ const collection = `${namespace}_${name}`.toLowerCase()
60
+
61
+ return Database.client.db('toa-dev').collection(collection)
62
+ }
56
63
  }
57
64
 
58
- type Document = Record<string, string | number>
65
+ type Document = Record<string, string | number | null>
@@ -14,7 +14,8 @@ export class IdP {
14
14
  private static privateKey?: crypto.KeyObject
15
15
  private static issuer?: string
16
16
 
17
- public constructor (private readonly captures: Captures) {}
17
+ public constructor (private readonly captures: Captures) {
18
+ }
18
19
 
19
20
  @afterAll()
20
21
  public static async stop (): Promise<void> {
@@ -29,14 +30,21 @@ export class IdP {
29
30
  if (IdP.server instanceof http.Server) return
30
31
 
31
32
  // creating the key
32
- const { publicKey, privateKey } = await util.promisify(crypto.generateKeyPair)('rsa', {
33
+ const {
34
+ publicKey,
35
+ privateKey
36
+ } = await util.promisify(crypto.generateKeyPair)('rsa', {
33
37
  modulusLength: 2048
34
38
  })
35
39
 
36
40
  IdP.privateKey = privateKey
37
41
 
38
42
  const jwk = JSON.stringify({
39
- keys: [{ use: 'sig', alg: 'RS256', ...publicKey.export({ format: 'jwk' }) }]
43
+ keys: [{
44
+ use: 'sig',
45
+ alg: 'RS256',
46
+ ...publicKey.export({ format: 'jwk' })
47
+ }]
40
48
  })
41
49
 
42
50
  const JWK_URL = '/.well-known/jwks'
@@ -54,24 +62,23 @@ export class IdP {
54
62
  response.end(jwk)
55
63
  break
56
64
 
57
- case '/.well-known/openid-configuration':
58
- {
59
- const openIdConfiguration = JSON.stringify({
60
- issuer: IdP.issuer,
61
- jwks_uri: IdP.issuer + JWK_URL,
62
- response_types_supported: ['id_token'],
63
- subject_types_supported: ['public'],
64
- id_token_signing_alg_values_supported: ['RS256'],
65
- scopes_supported: ['openid']
66
- })
67
-
68
- response.writeHead(200, {
69
- 'Content-Type': 'application/json',
70
- 'Cache-Control': 'public, max-age=3600',
71
- 'Content-Length': openIdConfiguration.length
72
- })
73
- response.end(openIdConfiguration)
74
- }
65
+ case '/.well-known/openid-configuration': {
66
+ const openIdConfiguration = JSON.stringify({
67
+ issuer: IdP.issuer,
68
+ jwks_uri: IdP.issuer + JWK_URL,
69
+ response_types_supported: ['id_token'],
70
+ subject_types_supported: ['public'],
71
+ id_token_signing_alg_values_supported: ['RS256'],
72
+ scopes_supported: ['openid']
73
+ })
74
+
75
+ response.writeHead(200, {
76
+ 'Content-Type': 'application/json',
77
+ 'Cache-Control': 'public, max-age=3600',
78
+ 'Content-Length': openIdConfiguration.length
79
+ })
80
+ response.end(openIdConfiguration)
81
+ }
75
82
 
76
83
  break
77
84
 
@@ -120,8 +127,6 @@ export class IdP {
120
127
 
121
128
  @given('the IDP {word} token for {word} is issued with following secret:')
122
129
  public async issueSymmetricToken (alg: string, user: string, secret: string): Promise<void> {
123
- console.log('Sym token for %s with secret "%s"', user, secret)
124
-
125
130
  const jwt = [
126
131
  {
127
132
  typ: 'JWT',
@@ -1,5 +1,4 @@
1
1
  name: echo
2
- version: 0.0.0
3
2
 
4
3
  operations:
5
4
  compute:
@@ -8,3 +7,8 @@ operations:
8
7
  affect:
9
8
  input:
10
9
  name*: string
10
+ identity:
11
+ input:
12
+ identity:
13
+ id: string
14
+ roles: [string]
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function computation (input) {
4
+ return input
5
+ }
6
+
7
+ exports.computation = computation
@@ -1,5 +1,4 @@
1
1
  name: users
2
- version: 0.0.0
3
2
 
4
3
  entity:
5
4
  schema:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.12",
3
+ "version": "1.0.0-alpha.14",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -17,9 +17,9 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@toa.io/core": "1.0.0-alpha.12",
21
- "@toa.io/generic": "1.0.0-alpha.12",
22
- "@toa.io/schemas": "1.0.0-alpha.12",
20
+ "@toa.io/core": "1.0.0-alpha.14",
21
+ "@toa.io/generic": "1.0.0-alpha.14",
22
+ "@toa.io/schemas": "1.0.0-alpha.14",
23
23
  "bcryptjs": "2.4.3",
24
24
  "error-value": "0.3.0",
25
25
  "js-yaml": "4.1.0",
@@ -34,20 +34,22 @@
34
34
  },
35
35
  "scripts": {
36
36
  "test": "jest",
37
- "transpile": "tsc && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation",
37
+ "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation",
38
+ "transpile:bans": "tsc -p ./components/identity.bans",
38
39
  "transpile:basic": "tsc -p ./components/identity.basic",
39
40
  "transpile:tokens": "tsc -p ./components/identity.tokens",
40
41
  "transpile:roles": "tsc -p ./components/identity.roles",
41
42
  "pretranspile:federation": "js-yaml components/identity.federation/manifest.toa.yaml | jq -M '{ type: \"object\", properties: {configuration: .configuration.schema, entity: .entity.schema }, additionalProperties: false}' > schemas.json && json2ts -i schemas.json -o components/identity.federation/source/schemas.ts && rm schemas.json",
42
43
  "transpile:federation": "tsc -p ./components/identity.federation",
43
- "features": "cucumber-js"
44
+ "features": "cucumber-js",
45
+ "features:security": "cucumber-js --tags @security"
44
46
  },
45
47
  "devDependencies": {
46
- "@toa.io/agent": "1.0.0-alpha.12",
47
- "@toa.io/extensions.storages": "1.0.0-alpha.12",
48
+ "@toa.io/agent": "1.0.0-alpha.14",
49
+ "@toa.io/extensions.storages": "1.0.0-alpha.14",
48
50
  "@types/bcryptjs": "2.4.3",
49
51
  "@types/cors": "2.8.13",
50
52
  "@types/negotiator": "0.6.1"
51
53
  },
52
- "gitHead": "897206fbcf724fa88f427b6aee35bff571b2a3a1"
54
+ "gitHead": "8aa52cb97021695885c8dbe64beca26c9665fc8f"
53
55
  }
@@ -93,5 +93,7 @@ export const shortcuts: RTD.syntax.Shortcuts = new Map([
93
93
  ['id', 'auth:id'],
94
94
  ['role', 'auth:role'],
95
95
  ['rule', 'auth:rule'],
96
- ['incept', 'auth:incept']
96
+ ['incept', 'auth:incept'],
97
+ ['input', 'io:input'],
98
+ ['output', 'io:output']
97
99
  ])
package/source/Gateway.ts CHANGED
@@ -31,7 +31,7 @@ export class Gateway extends Connector {
31
31
  const match = this.tree.match(context.url.pathname)
32
32
 
33
33
  if (match === null)
34
- throw new http.NotFound()
34
+ throw new http.NotFound('Route not found')
35
35
 
36
36
  const {
37
37
  node,
@@ -37,7 +37,7 @@ export class NotFound extends ClientError {
37
37
  }
38
38
 
39
39
  export class Conflict extends ClientError {
40
- public constructor (body: any) {
40
+ public constructor (body?: any) {
41
41
  super(409, body)
42
42
  }
43
43
  }
@@ -33,7 +33,7 @@ function createRoute (route: syntax.Route, context: Context): Route {
33
33
  }
34
34
 
35
35
  function createMethod (method: syntax.Method, context: Context): Method {
36
- const stack = context.directives.stack.concat(method.directives.reverse())
36
+ const stack = method.directives.concat(context.directives.stack)
37
37
  const directives = context.directives.factory.create(stack)
38
38
 
39
39
  const endpoint = method.mapping?.endpoint === undefined
package/source/Tenant.ts CHANGED
@@ -25,14 +25,6 @@ export class Tenant extends Connector {
25
25
  public override async open (): Promise<void> {
26
26
  await this.expose()
27
27
  await this.broadcast.receive('ping', this.expose.bind(this))
28
-
29
- console.info('Exposition Tenant for ' +
30
- `'${this.branch.namespace}.${this.branch.component}' has started.`)
31
- }
32
-
33
- public override async dispose (): Promise<void> {
34
- console.info('Exposition Tenant for ' +
35
- `'${this.branch.namespace}.${this.branch.component}' has been stopped.`)
36
28
  }
37
29
 
38
30
  private async expose (): Promise<void> {
@@ -39,7 +39,7 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
39
39
 
40
40
  public create (name: string, value: any, remotes: Remotes): Directive {
41
41
  assert.ok(name in constructors,
42
- `Directive '${name}' is not provided by the '${this.name}' family.`)
42
+ `Directive 'auth:${name}' is not implemented.`)
43
43
 
44
44
  const Class = constructors[name]
45
45
 
@@ -19,6 +19,9 @@ export class Delegate implements Directive {
19
19
  }
20
20
 
21
21
  private embed (body: unknown, identity: Identity): Record<string, unknown> {
22
+ if (body === undefined)
23
+ body = {}
24
+
22
25
  check(body)
23
26
  body[this.property] = identity
24
27
 
@@ -28,5 +31,5 @@ export class Delegate implements Directive {
28
31
 
29
32
  function check (body: unknown): asserts body is Record<string, unknown> {
30
33
  if (typeof body !== 'object' || body === null)
31
- throw new BadRequest('Invalid request body.')
34
+ throw new BadRequest('Invalid request body')
32
35
  }