@toa.io/extensions.exposition 1.0.0-alpha.101 → 1.0.0-alpha.103

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.basic/manifest.toa.yaml +9 -0
  2. package/components/identity.basic/operations/authenticate.js +2 -2
  3. package/components/identity.basic/operations/authenticate.js.map +1 -1
  4. package/components/identity.basic/operations/incept.js +1 -1
  5. package/components/identity.basic/operations/incept.js.map +1 -1
  6. package/components/identity.basic/operations/transit.js +3 -3
  7. package/components/identity.basic/operations/transit.js.map +1 -1
  8. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  9. package/components/identity.basic/source/authenticate.ts +2 -2
  10. package/components/identity.basic/source/incept.ts +1 -1
  11. package/components/identity.basic/source/transit.ts +3 -3
  12. package/components/identity.federation/operations/authenticate.js +1 -1
  13. package/components/identity.federation/operations/authenticate.js.map +1 -1
  14. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  15. package/components/identity.federation/source/authenticate.ts +1 -1
  16. package/components/identity.keys/manifest.toa.yaml +54 -0
  17. package/components/identity.keys/operations/create.d.ts +22 -0
  18. package/components/identity.keys/operations/create.js +16 -0
  19. package/components/identity.keys/operations/create.js.map +1 -0
  20. package/components/identity.keys/operations/tsconfig.tsbuildinfo +1 -0
  21. package/components/identity.keys/source/create.ts +35 -0
  22. package/components/identity.keys/tsconfig.json +9 -0
  23. package/components/identity.roles/manifest.toa.yaml +2 -0
  24. package/components/identity.roles/operations/grant.js +2 -2
  25. package/components/identity.roles/operations/grant.js.map +1 -1
  26. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  27. package/components/identity.roles/source/grant.ts +2 -2
  28. package/components/identity.tokens/manifest.toa.yaml +91 -11
  29. package/components/identity.tokens/operations/authenticate.d.ts +2 -2
  30. package/components/identity.tokens/operations/authenticate.js +12 -12
  31. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  32. package/components/identity.tokens/operations/decrypt.d.ts +12 -3
  33. package/components/identity.tokens/operations/decrypt.js +62 -18
  34. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  35. package/components/identity.tokens/operations/encrypt.d.ts +3 -3
  36. package/components/identity.tokens/operations/encrypt.js +24 -8
  37. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  38. package/components/identity.tokens/operations/issue.d.ts +24 -0
  39. package/components/identity.tokens/operations/issue.js +58 -0
  40. package/components/identity.tokens/operations/issue.js.map +1 -0
  41. package/components/identity.tokens/operations/lib/index.d.ts +2 -0
  42. package/components/identity.tokens/operations/lib/index.js +19 -0
  43. package/components/identity.tokens/operations/lib/index.js.map +1 -0
  44. package/components/identity.tokens/operations/lib/pad.d.ts +1 -0
  45. package/components/identity.tokens/operations/lib/pad.js +5 -0
  46. package/components/identity.tokens/operations/lib/pad.js.map +1 -0
  47. package/components/identity.tokens/operations/{types.d.ts → lib/types.d.ts} +35 -7
  48. package/components/identity.tokens/operations/lib/types.js.map +1 -0
  49. package/components/identity.tokens/operations/revoke.d.ts +2 -2
  50. package/components/identity.tokens/operations/revoke.js.map +1 -1
  51. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  52. package/components/identity.tokens/source/authenticate.test.ts +13 -7
  53. package/components/identity.tokens/source/authenticate.ts +14 -14
  54. package/components/identity.tokens/source/decrypt.test.ts +29 -16
  55. package/components/identity.tokens/source/decrypt.ts +90 -20
  56. package/components/identity.tokens/source/encrypt.test.ts +44 -13
  57. package/components/identity.tokens/source/encrypt.ts +36 -12
  58. package/components/identity.tokens/source/issue.ts +80 -0
  59. package/components/identity.tokens/source/lib/index.ts +2 -0
  60. package/components/identity.tokens/source/lib/pad.ts +1 -0
  61. package/components/identity.tokens/source/lib/paseto.test.ts +16 -0
  62. package/components/identity.tokens/source/{types.ts → lib/types.ts} +37 -7
  63. package/components/identity.tokens/source/revoke.ts +2 -2
  64. package/components/octets.storage/operations/put.js +4 -4
  65. package/documentation/components.md +77 -39
  66. package/features/identity.tokens.feature +0 -43
  67. package/features/identtiy.tokens.custom.feature +247 -0
  68. package/features/octets.cloudinary.feature +2 -2
  69. package/features/steps/Gateway.ts +3 -1
  70. package/package.json +7 -4
  71. package/source/directives/auth/Authorization.ts +30 -18
  72. package/source/directives/auth/Delegate.ts +1 -3
  73. package/source/directives/auth/Role.ts +4 -8
  74. package/source/directives/auth/types.ts +2 -1
  75. package/source/directives/octets/Put.ts +3 -19
  76. package/transpiled/directives/auth/Authorization.d.ts +2 -1
  77. package/transpiled/directives/auth/Authorization.js +25 -16
  78. package/transpiled/directives/auth/Authorization.js.map +1 -1
  79. package/transpiled/directives/auth/Delegate.js +1 -2
  80. package/transpiled/directives/auth/Delegate.js.map +1 -1
  81. package/transpiled/directives/auth/Role.d.ts +1 -1
  82. package/transpiled/directives/auth/Role.js +3 -5
  83. package/transpiled/directives/auth/Role.js.map +1 -1
  84. package/transpiled/directives/auth/types.d.ts +2 -1
  85. package/transpiled/directives/octets/Put.d.ts +0 -1
  86. package/transpiled/directives/octets/Put.js +0 -11
  87. package/transpiled/directives/octets/Put.js.map +1 -1
  88. package/transpiled/tsconfig.tsbuildinfo +1 -1
  89. package/components/identity.tokens/operations/types.js.map +0 -1
  90. /package/components/identity.tokens/operations/{types.js → lib/types.js} +0 -0
@@ -0,0 +1,247 @@
1
+ @security
2
+ Feature: Custom tokens
3
+
4
+ Background:
5
+ Given the `identity.basic` database contains:
6
+ | _id | authority | username | password |
7
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
8
+ And the `identity.roles` database contains:
9
+ | _id | identity | role |
10
+ | 9c4702490ff84f2a9e1b1da2ab64bdd4 | efe3a65ebbee47ed95a73edd911ea328 | app:notes |
11
+ And the `identity.keys` database is empty
12
+ And the annotation:
13
+ """yaml
14
+ /:
15
+ /notes:
16
+ auth:role: app:notes
17
+ GET:
18
+ io:output: true
19
+ dev:stub:
20
+ access: granted!
21
+ POST:
22
+ io:output: true
23
+ dev:stub:
24
+ access: granted!
25
+ /public:
26
+ GET:
27
+ auth:role: app:notes:public
28
+ io:output: true
29
+ dev:stub:
30
+ access: granted!
31
+ """
32
+
33
+ Scenario: Issuing a token
34
+ When the following request is received:
35
+ """
36
+ POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
37
+ host: nex.toa.io
38
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
39
+ content-type: application/yaml
40
+ accept: application/yaml
41
+
42
+ label: Dev token
43
+ lifetime: 600
44
+ """
45
+ Then the following reply is sent:
46
+ """
47
+ 201 Created
48
+
49
+ kid: ${{ kid }}
50
+ exp: ${{ exp }}
51
+ token: ${{ token }}
52
+ """
53
+ When the following request is received:
54
+ """
55
+ GET /identity/ HTTP/1.1
56
+ host: nex.toa.io
57
+ authorization: Token ${{ token }}
58
+ accept: application/yaml
59
+ """
60
+ Then the following reply is sent:
61
+ """
62
+ 200 OK
63
+
64
+ id: efe3a65ebbee47ed95a73edd911ea328
65
+ """
66
+
67
+ # debug LRU cache
68
+ When the following request is received:
69
+ """
70
+ GET /identity/ HTTP/1.1
71
+ host: nex.toa.io
72
+ authorization: Token ${{ token }}
73
+ """
74
+ Then the following reply is sent:
75
+ """
76
+ 200 OK
77
+ """
78
+
79
+ Scenario: Token with restricted scopes
80
+ When the following request is received:
81
+ """
82
+ POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
83
+ host: nex.toa.io
84
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
85
+ accept: application/yaml
86
+ content-type: application/yaml
87
+
88
+ label: Production token
89
+ lifetime: 0
90
+ scopes: [app:notes:public]
91
+ """
92
+ Then the following reply is sent:
93
+ """
94
+ 201 Created
95
+
96
+ token: ${{ token }}
97
+ """
98
+ When the following request is received:
99
+ """
100
+ GET /notes/ HTTP/1.1
101
+ host: nex.toa.io
102
+ authorization: Token ${{ token }}
103
+ """
104
+ Then the following reply is sent:
105
+ """
106
+ 403 Forbidden
107
+ """
108
+ When the following request is received:
109
+ """
110
+ GET /notes/public/ HTTP/1.1
111
+ host: nex.toa.io
112
+ authorization: Token ${{ token }}
113
+ """
114
+ Then the following reply is sent:
115
+ """
116
+ 200 OK
117
+ """
118
+
119
+ Scenario: Token with restricted permissions
120
+ When the following request is received:
121
+ """
122
+ POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
123
+ host: nex.toa.io
124
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
125
+ accept: application/yaml
126
+ content-type: application/yaml
127
+
128
+ label: Restricted token
129
+ lifetime: 0
130
+ permissions: {
131
+ /notes/: [GET]
132
+ }
133
+ """
134
+ Then the following reply is sent:
135
+ """
136
+ 201 Created
137
+
138
+ token: ${{ token }}
139
+ """
140
+ When the following request is received:
141
+ """
142
+ GET /notes/ HTTP/1.1
143
+ host: nex.toa.io
144
+ authorization: Token ${{ token }}
145
+ """
146
+ Then the following reply is sent:
147
+ """
148
+ 200 OK
149
+ """
150
+
151
+ # method is not permitted
152
+ When the following request is received:
153
+ """
154
+ POST /notes/ HTTP/1.1
155
+ host: nex.toa.io
156
+ authorization: Token ${{ token }}
157
+ """
158
+ Then the following reply is sent:
159
+ """
160
+ 403 Forbidden
161
+ """
162
+
163
+ # resource is not permitted
164
+ When the following request is received:
165
+ """
166
+ GET /notes/public/ HTTP/1.1
167
+ host: nex.toa.io
168
+ authorization: Token ${{ token }}
169
+ """
170
+ Then the following reply is sent:
171
+ """
172
+ 403 Forbidden
173
+ """
174
+
175
+ Scenario: Token revocation
176
+ Given the `identity.tokens` configuration:
177
+ """yaml
178
+ cache:
179
+ ttl: 1
180
+ """
181
+ When the following request is received:
182
+ """
183
+ POST /identity/tokens/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
184
+ host: nex.toa.io
185
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
186
+ accept: application/yaml
187
+ content-type: application/yaml
188
+
189
+ label: One-time token
190
+ lifetime: 60
191
+ """
192
+ Then the following reply is sent:
193
+ """
194
+ 201 Created
195
+
196
+ token: ${{ token }}
197
+ """
198
+ When the following request is received:
199
+ """
200
+ GET /identity/ HTTP/1.1
201
+ host: nex.toa.io
202
+ authorization: Token ${{ token }}
203
+ accept: application/yaml
204
+ """
205
+ Then the following reply is sent:
206
+ """
207
+ 200 OK
208
+
209
+ id: efe3a65ebbee47ed95a73edd911ea328
210
+ """
211
+ When the following request is received:
212
+ """
213
+ GET /identity/keys/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
214
+ host: nex.toa.io
215
+ authorization: Token ${{ token }}
216
+ accept: application/yaml
217
+ """
218
+ Then the following reply is sent:
219
+ """
220
+ 200 OK
221
+
222
+ - id: ${{ kid }}
223
+ label: One-time token
224
+ expires: ${{ expires }}
225
+ _created: ${{ created }}
226
+ """
227
+ When the following request is received:
228
+ """
229
+ DELETE /identity/keys/efe3a65ebbee47ed95a73edd911ea328/${{ kid }}/ HTTP/1.1
230
+ host: nex.toa.io
231
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
232
+ """
233
+ Then the following reply is sent:
234
+ """
235
+ 200 OK
236
+ """
237
+ And after 1 second
238
+ When the following request is received:
239
+ """
240
+ GET /identity/ HTTP/1.1
241
+ host: nex.toa.io
242
+ authorization: Token ${{ token }}
243
+ """
244
+ Then the following reply is sent:
245
+ """
246
+ 401 Unauthorized
247
+ """
@@ -29,13 +29,13 @@ Feature: Octets with Cloudinary storage
29
29
  201 Created
30
30
  content-type: application/yaml
31
31
 
32
- id: 814a0034f5549e957ee61360d87457e5
32
+ id: ${{ id }}
33
33
  type: image/png
34
34
  size: 473831
35
35
  """
36
36
  When the following request is received:
37
37
  """
38
- GET /814a0034f5549e957ee61360d87457e5 HTTP/1.1
38
+ GET /${{ id }} HTTP/1.1
39
39
  host: nex.toa.io
40
40
  """
41
41
  Then the stream equals to `lenna.png` is sent with the following headers:
@@ -127,6 +127,8 @@ const DEFAULT_PROPERTIES: Partial<http.Options> = {
127
127
 
128
128
  const DEFAULT_CONFIGURATION: Record<string, object> = {
129
129
  'identity.tokens': {
130
- key0: 'k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA'
130
+ keys: {
131
+ 0: 'k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA'
132
+ }
131
133
  }
132
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.101",
3
+ "version": "1.0.0-alpha.103",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -22,10 +22,12 @@
22
22
  "@toa.io/generic": "1.0.0-alpha.93",
23
23
  "@toa.io/schemas": "1.0.0-alpha.93",
24
24
  "bcryptjs": "2.4.3",
25
- "error-value": "0.3.0",
25
+ "error-value": "0.4.1",
26
26
  "http-cache-semantics": "4.1.1",
27
27
  "js-yaml": "4.1.0",
28
+ "lru-cache": "11.0.1",
28
29
  "matchacho": "0.3.5",
30
+ "minimatch": "10.0.1",
29
31
  "msgpackr": "1.10.1",
30
32
  "negotiator": "0.6.3",
31
33
  "openspan": "1.0.0-alpha.93",
@@ -39,12 +41,13 @@
39
41
  },
40
42
  "scripts": {
41
43
  "test": "jest",
42
- "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation",
44
+ "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation && npm run transpile:keys",
43
45
  "transpile:bans": "tsc -p ./components/identity.bans",
44
46
  "transpile:basic": "tsc -p ./components/identity.basic",
45
47
  "transpile:tokens": "tsc -p ./components/identity.tokens",
46
48
  "transpile:roles": "tsc -p ./components/identity.roles",
47
49
  "transpile:federation": "tsc -p ./components/identity.federation",
50
+ "transpile:keys": "tsc -p ./components/identity.keys",
48
51
  "features": "cucumber-js",
49
52
  "features:security": "cucumber-js --tags @security",
50
53
  "features:octets": "cucumber-js features/octets.*"
@@ -58,5 +61,5 @@
58
61
  "@types/negotiator": "0.6.1",
59
62
  "jest-esbuild": "0.3.0"
60
63
  },
61
- "gitHead": "19a5855caa247439f17960dfdc25053b5eca6395"
64
+ "gitHead": "a52ca7d2b22f5855e2f539f5fb3425854ba476c7"
62
65
  }
@@ -1,5 +1,7 @@
1
1
  import assert from 'node:assert'
2
2
  import { match } from 'matchacho'
3
+ import { console } from 'openspan'
4
+ import { minimatch } from 'minimatch'
3
5
  import * as http from '../../HTTP'
4
6
  import { Anonymous } from './Anonymous'
5
7
  import { Id } from './Id'
@@ -57,27 +59,21 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
57
59
  }
58
60
 
59
61
  public async preflight (directives: Directive[],
60
- input: Input,
62
+ context: Input,
61
63
  parameters: Parameter[]): Promise<Output> {
62
- /**
63
- * Some authentication scheme providers may create identity during authentication;
64
- * therefore, we need to skip the authentication process if the Incept directive is present.
65
- *
66
- * If the provided credentials already exist,
67
- * the inception will cause a unique constraint violation on the settle stage.
68
- */
69
- // const inception = directives.reduce((yes, directive) => yes || directive instanceof Incept, false)
70
-
71
- input.identity = await this.resolve(input.authority, input.request.headers.authorization)
64
+ context.identity = await this.resolve(context.authority, context.request.headers.authorization)
72
65
 
73
66
  for (const directive of directives) {
74
- const allow = await directive.authorize(input.identity, input, parameters)
67
+ const allow = await directive.authorize(context.identity, context, parameters)
75
68
 
76
69
  if (allow)
77
- return directive.reply?.(input.identity) ?? null
70
+ if (this.permitted(context))
71
+ return directive.reply?.(context.identity) ?? null
72
+ else
73
+ throw new http.Forbidden()
78
74
  }
79
75
 
80
- if (input.identity === null)
76
+ if (context.identity === null)
81
77
  throw new http.Unauthorized()
82
78
  else
83
79
  throw new http.Forbidden()
@@ -98,9 +94,7 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
98
94
  return
99
95
 
100
96
  // Role directive may have already set the value
101
- if (identity.roles === undefined)
102
- await Role.set(identity, this.discovery.roles)
103
-
97
+ identity.roles ??= await Role.get(identity, this.discovery.roles)
104
98
  this.tokens ??= await this.discovery.tokens
105
99
 
106
100
  const token = await this.tokens.invoke<string>('encrypt', {
@@ -132,8 +126,14 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
132
126
  }
133
127
  })
134
128
 
135
- if (result instanceof Error)
129
+ if (result instanceof Error) {
130
+ const code: string | unknown = (result as unknown as { code: string }).code
131
+
132
+ if (typeof code === 'string')
133
+ console.info('Authentication failed', { code })
134
+
136
135
  return null
136
+ }
137
137
 
138
138
  const identity = result.identity
139
139
 
@@ -145,6 +145,18 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
145
145
  return identity
146
146
  }
147
147
 
148
+ private permitted (context: Input): boolean {
149
+ const permissions = context.identity?.permissions
150
+
151
+ if (permissions === undefined)
152
+ return true
153
+
154
+ return Object.entries(permissions).some(([pattern, methods]) => {
155
+ return methods.some((method) => method === '*' || method === context.request.method) &&
156
+ minimatch(context.request.url, pattern)
157
+ })
158
+ }
159
+
148
160
  private async banned (identity: Identity): Promise<boolean> {
149
161
  this.bans ??= await this.discovery.bans
150
162
 
@@ -17,9 +17,7 @@ export class Delegate implements Directive {
17
17
  if (identity === null)
18
18
  return false
19
19
 
20
- if (identity.roles === undefined)
21
- await Role.set(identity, this.discovery)
22
-
20
+ identity.roles ??= await Role.get(identity, this.discovery)
23
21
  context.pipelines.body.push((body) => this.embed(body, identity))
24
22
 
25
23
  return true
@@ -15,7 +15,7 @@ export class Role implements Directive {
15
15
  this.dynamic = this.roles.some((role) => role.includes('{'))
16
16
  }
17
17
 
18
- public static async set (identity: Identity, discovery: Promise<Component>): Promise<void> {
18
+ public static async get (identity: Identity, discovery: Promise<Component>): Promise<string[]> {
19
19
  this.remote ??= await discovery
20
20
 
21
21
  const query: Query = {
@@ -23,7 +23,7 @@ export class Role implements Directive {
23
23
  limit: 1024
24
24
  }
25
25
 
26
- identity.roles = await this.remote.invoke('list', { query })
26
+ return await this.remote.invoke('list', { query })
27
27
  }
28
28
 
29
29
  public async authorize
@@ -31,13 +31,9 @@ export class Role implements Directive {
31
31
  if (identity === null)
32
32
  return false
33
33
 
34
- await Role.set(identity, this.discovery)
34
+ identity.roles ??= await Role.get(identity, this.discovery)
35
35
 
36
- if (identity.roles!.length === 0) // Role.set()
37
-
38
- return false
39
-
40
- return this.match(identity.roles!, parameters)
36
+ return this.match(identity.roles, parameters)
41
37
  }
42
38
 
43
39
  private match (roles: string[], parameters: Parameter[]): boolean {
@@ -18,8 +18,9 @@ export interface Directive {
18
18
 
19
19
  export interface Identity {
20
20
  readonly id: string
21
- scheme: string | null // null for transient identities
22
21
  roles?: string[]
22
+ permissions?: Record<string, string[]>
23
+ scheme: string | null // null for transient identities
23
24
  refresh: boolean
24
25
  }
25
26
 
@@ -11,7 +11,7 @@ import type { Parameter } from '../../RTD'
11
11
  import type { Unit } from './workflows'
12
12
  import type { Entry } from '@toa.io/extensions.storages'
13
13
  import type { Remotes } from '../../Remotes'
14
- import type { ErrorType } from 'error-value'
14
+ import type { Err } from 'error-value'
15
15
  import type { Component } from '@toa.io/core'
16
16
  import type { Output } from '../../io'
17
17
  import type { Input } from './types'
@@ -69,7 +69,7 @@ export class Put extends Directive {
69
69
  const entry = await this.storage.invoke<Entry>('put', request)
70
70
 
71
71
  return match<Output>(entry,
72
- Error, (error: ErrorType) => this.throw(error),
72
+ Error, (error: Err) => this.throw(error),
73
73
  () => this.reply(input, storage, entry, parameters))
74
74
  }
75
75
 
@@ -94,7 +94,7 @@ export class Put extends Directive {
94
94
  return stream
95
95
  }
96
96
 
97
- private throw (error: ErrorType): never {
97
+ private throw (error: Err): never {
98
98
  throw match(error.code,
99
99
  'NOT_ACCEPTABLE', () => new http.UnsupportedMediaType(),
100
100
  'TYPE_MISMATCH', () => new http.BadRequest(),
@@ -105,22 +105,6 @@ export class Put extends Directive {
105
105
  'INVALID_ID', () => new http.BadRequest(error.message),
106
106
  error)
107
107
  }
108
-
109
- private attributes (value: string | string[]): Record<string, string> {
110
- if (Array.isArray(value))
111
- value = value.join(',')
112
-
113
- const attributes: Record<string, string> = {}
114
-
115
- for (const pair of value.split(',')) {
116
- const eq = pair.indexOf('=')
117
- const key = (eq === -1 ? pair : pair.slice(0, eq)).trim()
118
-
119
- attributes[key] = eq === -1 ? 'true' : pair.slice(eq + 1).trim()
120
- }
121
-
122
- return attributes
123
- }
124
108
  }
125
109
 
126
110
  export interface Options {
@@ -12,8 +12,9 @@ export declare class Authorization implements DirectiveFamily<Directive, Extensi
12
12
  private tokens;
13
13
  private bans;
14
14
  create(name: string, value: any, remotes: Remotes): Directive;
15
- preflight(directives: Directive[], input: Input, parameters: Parameter[]): Promise<Output>;
15
+ preflight(directives: Directive[], context: Input, parameters: Parameter[]): Promise<Output>;
16
16
  settle(directives: Directive[], input: Input, response: http.OutgoingMessage): Promise<void>;
17
17
  private resolve;
18
+ private permitted;
18
19
  private banned;
19
20
  }
@@ -29,6 +29,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.Authorization = void 0;
30
30
  const node_assert_1 = __importDefault(require("node:assert"));
31
31
  const matchacho_1 = require("matchacho");
32
+ const openspan_1 = require("openspan");
33
+ const minimatch_1 = require("minimatch");
32
34
  const http = __importStar(require("../../HTTP"));
33
35
  const Anonymous_1 = require("./Anonymous");
34
36
  const Id_1 = require("./Id");
@@ -57,22 +59,17 @@ class Authorization {
57
59
  this.discovery[name] ??= remotes.discover('identity', name);
58
60
  return (0, matchacho_1.match)(Class, Role_1.Role, () => new Role_1.Role(value, this.discovery.roles), Rule_1.Rule, () => new Rule_1.Rule(value, this.create.bind(this)), Incept_1.Incept, () => new Incept_1.Incept(value, this.discovery), Delegate_1.Delegate, () => new Delegate_1.Delegate(value, this.discovery.roles), () => new Class(value));
59
61
  }
60
- async preflight(directives, input, parameters) {
61
- /**
62
- * Some authentication scheme providers may create identity during authentication;
63
- * therefore, we need to skip the authentication process if the Incept directive is present.
64
- *
65
- * If the provided credentials already exist,
66
- * the inception will cause a unique constraint violation on the settle stage.
67
- */
68
- // const inception = directives.reduce((yes, directive) => yes || directive instanceof Incept, false)
69
- input.identity = await this.resolve(input.authority, input.request.headers.authorization);
62
+ async preflight(directives, context, parameters) {
63
+ context.identity = await this.resolve(context.authority, context.request.headers.authorization);
70
64
  for (const directive of directives) {
71
- const allow = await directive.authorize(input.identity, input, parameters);
65
+ const allow = await directive.authorize(context.identity, context, parameters);
72
66
  if (allow)
73
- return directive.reply?.(input.identity) ?? null;
67
+ if (this.permitted(context))
68
+ return directive.reply?.(context.identity) ?? null;
69
+ else
70
+ throw new http.Forbidden();
74
71
  }
75
- if (input.identity === null)
72
+ if (context.identity === null)
76
73
  throw new http.Unauthorized();
77
74
  else
78
75
  throw new http.Forbidden();
@@ -85,8 +82,7 @@ class Authorization {
85
82
  if (identity.scheme === schemes_1.PRIMARY && !identity.refresh)
86
83
  return;
87
84
  // Role directive may have already set the value
88
- if (identity.roles === undefined)
89
- await Role_1.Role.set(identity, this.discovery.roles);
85
+ identity.roles ??= await Role_1.Role.get(identity, this.discovery.roles);
90
86
  this.tokens ??= await this.discovery.tokens;
91
87
  const token = await this.tokens.invoke('encrypt', {
92
88
  input: { authority: input.authority, identity }
@@ -109,8 +105,12 @@ class Authorization {
109
105
  credentials
110
106
  }
111
107
  });
112
- if (result instanceof Error)
108
+ if (result instanceof Error) {
109
+ const code = result.code;
110
+ if (typeof code === 'string')
111
+ openspan_1.console.info('Authentication failed', { code });
113
112
  return null;
113
+ }
114
114
  const identity = result.identity;
115
115
  if (scheme !== schemes_1.PRIMARY && (await this.banned(identity)))
116
116
  throw new http.Unauthorized();
@@ -118,6 +118,15 @@ class Authorization {
118
118
  identity.refresh = result.refresh;
119
119
  return identity;
120
120
  }
121
+ permitted(context) {
122
+ const permissions = context.identity?.permissions;
123
+ if (permissions === undefined)
124
+ return true;
125
+ return Object.entries(permissions).some(([pattern, methods]) => {
126
+ return methods.some((method) => method === '*' || method === context.request.method) &&
127
+ (0, minimatch_1.minimatch)(context.request.url, pattern);
128
+ });
129
+ }
121
130
  async banned(identity) {
122
131
  this.bans ??= await this.discovery.bans;
123
132
  const ban = await this.bans.invoke('observe', { query: { id: identity.id } });
@@ -1 +1 @@
1
- {"version":3,"file":"Authorization.js","sourceRoot":"","sources":["../../../source/directives/auth/Authorization.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Authorization.js","sourceRoot":"","sources":["../../../source/directives/auth/Authorization.ts"],"names":[],"mappings":"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"}
@@ -13,8 +13,7 @@ class Delegate {
13
13
  async authorize(identity, context) {
14
14
  if (identity === null)
15
15
  return false;
16
- if (identity.roles === undefined)
17
- await Role_1.Role.set(identity, this.discovery);
16
+ identity.roles ??= await Role_1.Role.get(identity, this.discovery);
18
17
  context.pipelines.body.push((body) => this.embed(body, identity));
19
18
  return true;
20
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Delegate.js","sourceRoot":"","sources":["../../../source/directives/auth/Delegate.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAEvC,iCAA6B;AAI7B,MAAa,QAAQ;IACF,QAAQ,CAAQ;IAChB,SAAS,CAAoB;IAE9C,YAAoB,QAAgB,EAAE,SAA6B;QACjE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAEM,KAAK,CAAC,SAAS,CAAE,QAAyB,EAAE,OAAc;QAC/D,IAAI,QAAQ,KAAK,IAAI;YACnB,OAAO,KAAK,CAAA;QAEd,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS;YAC9B,MAAM,WAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;QAE1C,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAA;QAEjE,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,KAAK,CAAE,IAAa,EAAE,QAAkB;QAC9C,IAAI,IAAI,KAAK,SAAS;YACpB,IAAI,GAAG,EAAE,CAAA;QAEX,KAAK,CAAC,IAAI,CAAC,CAAA;QACX,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;QAE/C,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AA9BD,4BA8BC;AAED,SAAS,KAAK,CAAE,IAAa;IAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAC3C,MAAM,IAAI,iBAAU,CAAC,sBAAsB,CAAC,CAAA;AAChD,CAAC"}
1
+ {"version":3,"file":"Delegate.js","sourceRoot":"","sources":["../../../source/directives/auth/Delegate.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAEvC,iCAA6B;AAI7B,MAAa,QAAQ;IACF,QAAQ,CAAQ;IAChB,SAAS,CAAoB;IAE9C,YAAoB,QAAgB,EAAE,SAA6B;QACjE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAEM,KAAK,CAAC,SAAS,CAAE,QAAyB,EAAE,OAAc;QAC/D,IAAI,QAAQ,KAAK,IAAI;YACnB,OAAO,KAAK,CAAA;QAEd,QAAQ,CAAC,KAAK,KAAK,MAAM,WAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;QAC3D,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAA;QAEjE,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,KAAK,CAAE,IAAa,EAAE,QAAkB;QAC9C,IAAI,IAAI,KAAK,SAAS;YACpB,IAAI,GAAG,EAAE,CAAA;QAEX,KAAK,CAAC,IAAI,CAAC,CAAA;QACX,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;QAE/C,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AA5BD,4BA4BC;AAED,SAAS,KAAK,CAAE,IAAa;IAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAC3C,MAAM,IAAI,iBAAU,CAAC,sBAAsB,CAAC,CAAA;AAChD,CAAC"}
@@ -7,7 +7,7 @@ export declare class Role implements Directive {
7
7
  private readonly discovery;
8
8
  private readonly dynamic;
9
9
  constructor(roles: string | string[], discovery: Promise<Component>);
10
- static set(identity: Identity, discovery: Promise<Component>): Promise<void>;
10
+ static get(identity: Identity, discovery: Promise<Component>): Promise<string[]>;
11
11
  authorize(identity: Identity | null, _: unknown, parameters: Parameter[]): Promise<boolean>;
12
12
  private match;
13
13
  private substitute;