@toa.io/extensions.exposition 1.0.0-alpha.272 → 1.0.0-alpha.274

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 (156) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/components/exposition.atom/manifest.toa.yaml +3 -1
  3. package/components/exposition.octets/manifest.toa.yaml +28 -10
  4. package/components/exposition.octets/operations/put.js +20 -5
  5. package/components/identity.bans/manifest.toa.yaml +26 -7
  6. package/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -1
  7. package/components/identity.basic/manifest.toa.yaml +106 -30
  8. package/components/identity.basic/operations/authenticate.js +7 -4
  9. package/components/identity.basic/operations/authenticate.js.map +1 -1
  10. package/components/identity.basic/operations/incept.js +3 -2
  11. package/components/identity.basic/operations/incept.js.map +1 -1
  12. package/components/identity.basic/operations/transit.js +17 -6
  13. package/components/identity.basic/operations/transit.js.map +1 -1
  14. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  15. package/components/identity.basic/operations/types.d.ts +2 -2
  16. package/components/identity.basic/source/authenticate.ts +8 -4
  17. package/components/identity.basic/source/incept.ts +3 -2
  18. package/components/identity.basic/source/transit.ts +20 -6
  19. package/components/identity.basic/source/types.ts +2 -2
  20. package/components/identity.credentials/manifest.toa.yaml +34 -13
  21. package/components/identity.credentials/operations/tsconfig.tsbuildinfo +1 -1
  22. package/components/identity.federation/manifest.toa.yaml +120 -28
  23. package/components/identity.federation/operations/authenticate.js +3 -2
  24. package/components/identity.federation/operations/authenticate.js.map +1 -1
  25. package/components/identity.federation/operations/create.js +4 -2
  26. package/components/identity.federation/operations/create.js.map +1 -1
  27. package/components/identity.federation/operations/lib/errors.d.ts +70 -11
  28. package/components/identity.federation/operations/lib/errors.js +40 -11
  29. package/components/identity.federation/operations/lib/errors.js.map +1 -1
  30. package/components/identity.federation/operations/lib/exchange.js +2 -2
  31. package/components/identity.federation/operations/lib/exchange.js.map +1 -1
  32. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  33. package/components/identity.federation/operations/types/configuration.d.ts +3 -2
  34. package/components/identity.federation/source/authenticate.ts +3 -2
  35. package/components/identity.federation/source/create.ts +4 -2
  36. package/components/identity.federation/source/lib/errors.ts +49 -12
  37. package/components/identity.federation/source/lib/exchange.ts +2 -2
  38. package/components/identity.federation/source/types/configuration.ts +4 -2
  39. package/components/identity.keys/manifest.toa.yaml +14 -6
  40. package/components/identity.keys/operations/tsconfig.tsbuildinfo +1 -1
  41. package/components/identity.otp/manifest.toa.yaml +44 -15
  42. package/components/identity.otp/operations/authenticate.js +9 -4
  43. package/components/identity.otp/operations/authenticate.js.map +1 -1
  44. package/components/identity.otp/operations/tsconfig.tsbuildinfo +1 -1
  45. package/components/identity.otp/source/authenticate.ts +11 -4
  46. package/components/identity.passkeys/manifest.toa.yaml +38 -8
  47. package/components/identity.passkeys/operations/authenticate.js +3 -2
  48. package/components/identity.passkeys/operations/authenticate.js.map +1 -1
  49. package/components/identity.passkeys/operations/create.js +6 -3
  50. package/components/identity.passkeys/operations/create.js.map +1 -1
  51. package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
  52. package/components/identity.passkeys/operations/use.js +6 -3
  53. package/components/identity.passkeys/operations/use.js.map +1 -1
  54. package/components/identity.passkeys/source/authenticate.ts +3 -2
  55. package/components/identity.passkeys/source/create.ts +7 -3
  56. package/components/identity.passkeys/source/use.ts +7 -3
  57. package/components/identity.roles/manifest.toa.yaml +41 -10
  58. package/components/identity.roles/operations/grant.js +3 -2
  59. package/components/identity.roles/operations/grant.js.map +1 -1
  60. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  61. package/components/identity.roles/source/grant.ts +3 -2
  62. package/components/identity.tokens/manifest.toa.yaml +99 -39
  63. package/components/identity.tokens/operations/authenticate.js +6 -3
  64. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  65. package/components/identity.tokens/operations/decrypt.js +10 -5
  66. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  67. package/components/identity.tokens/operations/encrypt.js +4 -3
  68. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  69. package/components/identity.tokens/operations/lib/types.d.ts +3 -2
  70. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  71. package/components/identity.tokens/source/authenticate.test.ts +7 -2
  72. package/components/identity.tokens/source/authenticate.ts +7 -3
  73. package/components/identity.tokens/source/decrypt.test.ts +13 -7
  74. package/components/identity.tokens/source/decrypt.ts +12 -5
  75. package/components/identity.tokens/source/encrypt.test.ts +9 -4
  76. package/components/identity.tokens/source/encrypt.ts +4 -3
  77. package/components/identity.tokens/source/lib/types.ts +3 -2
  78. package/context.toa.yaml +2 -2
  79. package/documentation/components.md +1 -1
  80. package/documentation/identity.md +4 -1
  81. package/features/configuration.feature +207 -0
  82. package/features/cors.feature +1 -1
  83. package/features/dev.feature +1 -1
  84. package/features/identity.federation.feature +18 -9
  85. package/features/{identtiy.tokens.custom.feature → identity.tokens.custom.feature} +39 -0
  86. package/features/introspection.map.feature +16 -0
  87. package/features/map.feature +1 -1
  88. package/features/octets.download.feature +1 -1
  89. package/features/octets.meta.feature +1 -1
  90. package/features/passkeys.feature +8 -8
  91. package/features/require.feature +2 -2
  92. package/features/response.feature +3 -3
  93. package/features/steps/Components.ts +41 -4
  94. package/features/steps/Gateway.ts +61 -23
  95. package/features/steps/HTTP.ts +3 -5
  96. package/features/steps/Parameters.ts +2 -3
  97. package/features/steps/Realtime.ts +4 -4
  98. package/features/steps/Workspace.ts +8 -3
  99. package/features/steps/components/audit/manifest.toa.yaml +12 -5
  100. package/features/steps/components/echo/manifest.toa.yaml +33 -8
  101. package/features/steps/components/notify/manifest.toa.yaml +12 -5
  102. package/features/steps/components/octets.tester/manifest.toa.yaml +43 -14
  103. package/features/steps/components/orders/manifest.toa.yaml +22 -5
  104. package/features/steps/components/pots/manifest.toa.yaml +71 -17
  105. package/features/steps/components/pricing/manifest.toa.yaml +8 -2
  106. package/features/steps/components/sequences/manifest.toa.yaml +3 -1
  107. package/features/steps/components/users/manifest.toa.yaml +18 -3
  108. package/features/steps/components/users.properties/manifest.toa.yaml +12 -2
  109. package/package.json +5 -6
  110. package/schemas/annotation.cos.yaml +37 -17
  111. package/schemas/directive.cos.yaml +7 -3
  112. package/schemas/io/throttle.cos.yaml +47 -8
  113. package/schemas/method.cos.yaml +21 -9
  114. package/schemas/node.cos.yaml +22 -7
  115. package/schemas/octets/delete.cos.yaml +15 -2
  116. package/schemas/octets/get.cos.yaml +5 -2
  117. package/schemas/octets/put.cos.yaml +6 -3
  118. package/schemas/octets/workflow.cos.yaml +0 -1
  119. package/schemas/query.cos.yaml +31 -14
  120. package/schemas/querystring.cos.yaml +19 -9
  121. package/schemas/range.cos.yaml +10 -2
  122. package/schemas/route.cos.yaml +6 -2
  123. package/source/Branch.test.ts +37 -0
  124. package/source/Branch.ts +37 -0
  125. package/source/Factory.ts +5 -5
  126. package/source/Gateway.ts +20 -13
  127. package/source/HTTP/formats/yaml.test.ts +10 -2
  128. package/source/HTTP/formats/yaml.ts +21 -1
  129. package/source/RTD/Tree.ts +4 -1
  130. package/source/Tenant.ts +3 -3
  131. package/source/deployment.ts +3 -4
  132. package/source/directives/map/Headers.ts +8 -1
  133. package/source/directives/octets/Put.ts +6 -3
  134. package/source/root.ts +2 -2
  135. package/transpiled/Branch.d.ts +23 -0
  136. package/transpiled/Branch.js +16 -0
  137. package/transpiled/Branch.js.map +1 -1
  138. package/transpiled/Factory.js +4 -4
  139. package/transpiled/Factory.js.map +1 -1
  140. package/transpiled/Gateway.js +17 -7
  141. package/transpiled/Gateway.js.map +1 -1
  142. package/transpiled/HTTP/formats/yaml.js +15 -1
  143. package/transpiled/HTTP/formats/yaml.js.map +1 -1
  144. package/transpiled/RTD/Tree.js +4 -1
  145. package/transpiled/RTD/Tree.js.map +1 -1
  146. package/transpiled/Tenant.d.ts +1 -1
  147. package/transpiled/Tenant.js +1 -1
  148. package/transpiled/Tenant.js.map +1 -1
  149. package/transpiled/deployment.js +3 -4
  150. package/transpiled/deployment.js.map +1 -1
  151. package/transpiled/directives/map/Headers.js +7 -1
  152. package/transpiled/directives/map/Headers.js.map +1 -1
  153. package/transpiled/directives/octets/Put.js.map +1 -1
  154. package/transpiled/root.js +1 -1
  155. package/transpiled/root.js.map +1 -1
  156. package/transpiled/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,7 @@
1
1
  import { generate } from 'randomstring'
2
2
  import { Computation as Authenticate } from './authenticate'
3
3
  import type { Configuration, Context, DecryptOutput, Identity } from './lib'
4
+ import type { Secret } from '@toa.io/types'
4
5
 
5
6
  let configuration: Configuration
6
7
  let context: Context
@@ -13,8 +14,8 @@ const authority = generate()
13
14
  beforeEach(() => {
14
15
  configuration = {
15
16
  keys: [
16
- { id: 'key0', key: 'sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs' },
17
- { id: 'legacy0', key: 'k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog', format: 'paseto' }
17
+ { id: 'key0', key: secret('sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs') },
18
+ { id: 'legacy0', key: secret('k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog'), format: 'paseto' }
18
19
  ],
19
20
  lifetime: 2592000,
20
21
  refresh: 600,
@@ -68,3 +69,7 @@ it.each([true, false])('should return stale: %s',
68
69
 
69
70
  expect(result).toEqual({ identity, refresh })
70
71
  })
72
+
73
+ function secret (value: string): Secret {
74
+ return { unwrap: () => value }
75
+ }
@@ -1,4 +1,3 @@
1
- import { Err } from 'error-value'
2
1
  import type { Maybe, Operation } from '@toa.io/types'
3
2
  import type { AuthenticateInput, AuthenticateOutput, Context } from './lib'
4
3
 
@@ -43,5 +42,10 @@ export class Computation implements Operation {
43
42
  }
44
43
  }
45
44
 
46
- const ERR_AUTHORITY = new Err('AUTHORITY_MISMATCH')
47
- const ERR_TOKEN_REVOKED = new Err('TOKEN_REVOKED')
45
+ const ERR_AUTHORITY = new (class AuthorityMismatchError extends Error {
46
+ public readonly code = 'AUTHORITY_MISMATCH'
47
+ })()
48
+
49
+ const ERR_TOKEN_REVOKED = new (class TokenRevokedError extends Error {
50
+ public readonly code = 'TOKEN_REVOKED'
51
+ })()
@@ -3,6 +3,7 @@ import { V3 } from 'paseto'
3
3
  import { Effect as Encrypt } from './encrypt'
4
4
  import { Computation as Decrypt } from './decrypt'
5
5
  import { type Configuration, type Context, type Identity } from './lib'
6
+ import type { Secret } from '@toa.io/types'
6
7
 
7
8
  let configuration: Configuration
8
9
  let context: Context
@@ -15,10 +16,11 @@ const authority = generate()
15
16
  beforeEach(() => {
16
17
  configuration = {
17
18
  keys: [
18
- { id: 'key0', key: 'sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs' },
19
- { id: 'key1', key: '5I0iSKw3yfBkQ4AXfA8eR-tWR0Q1dpn4x3bPrPzHkP0' },
20
- { id: 'legacy0', key: 'k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog', format: 'paseto' },
21
- { id: 'legacy1', key: 'k3.local.-498jfWenrZH-Dqw3-zQJih_hKzDgBgUMfe37OCqSOA', format: 'paseto' }
19
+ { id: 'key0', key: secret('sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs') },
20
+ // keys are secrets, and a secret is an object
21
+ { id: 'key1', key: secret('5I0iSKw3yfBkQ4AXfA8eR-tWR0Q1dpn4x3bPrPzHkP0') },
22
+ { id: 'legacy0', key: secret('k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog'), format: 'paseto' },
23
+ { id: 'legacy1', key: secret('k3.local.-498jfWenrZH-Dqw3-zQJih_hKzDgBgUMfe37OCqSOA'), format: 'paseto' }
22
24
  ],
23
25
  lifetime: 1000,
24
26
  refresh: 500,
@@ -77,7 +79,7 @@ it('should decrypt with key1', async () => {
77
79
  it('should decrypt legacy PASETO and require refresh', async () => {
78
80
  const identity: Identity = { id: generate(), roles: [] }
79
81
 
80
- const token = await V3.encrypt({ iss: authority, identity }, configuration.keys[2].key, {
82
+ const token = await V3.encrypt({ iss: authority, identity }, configuration.keys[2].key.unwrap(), {
81
83
  footer: { kid: 'legacy0' }
82
84
  })
83
85
 
@@ -96,7 +98,7 @@ it('should separate JWE and PASETO keys with the same id by format', async () =>
96
98
 
97
99
  const identity: Identity = { id: generate(), roles: [] }
98
100
 
99
- const token = await V3.encrypt({ iss: authority, identity }, configuration.keys[2].key, {
101
+ const token = await V3.encrypt({ iss: authority, identity }, configuration.keys[2].key.unwrap(), {
100
102
  footer: { kid: 'key0' }
101
103
  })
102
104
 
@@ -125,7 +127,7 @@ it('should reject JWE with an unknown key', async () => {
125
127
  const token = await encrypt.execute({
126
128
  authority,
127
129
  identity: { id: generate(), roles: [] },
128
- key: { id: 'missing', key: configuration.keys[0].key, label: 'missing' }
130
+ key: { id: 'missing', key: configuration.keys[0].key.unwrap(), label: 'missing' }
129
131
  })
130
132
 
131
133
  if (token instanceof Error)
@@ -133,3 +135,7 @@ it('should reject JWE with an unknown key', async () => {
133
135
 
134
136
  await expect(decrypt.execute(token)).resolves.toMatchObject({ code: 'INVALID_KEY' })
135
137
  })
138
+
139
+ function secret (value: string): Secret {
140
+ return { unwrap: () => value }
141
+ }
@@ -1,5 +1,4 @@
1
1
  import { V3 } from 'paseto'
2
- import { Err } from 'error-value'
3
2
  import { LRUCache } from 'lru-cache'
4
3
  import { jweKey } from './lib'
5
4
  import { load } from './lib/jose'
@@ -28,7 +27,7 @@ export class Computation implements Operation {
28
27
  for (const { id, key, format } of context.configuration.keys) {
29
28
  const branch = format === 'paseto' ? this.legacy : this.keys
30
29
 
31
- branch[id] = { key }
30
+ branch[id] = { key: key.unwrap() }
32
31
  }
33
32
  }
34
33
 
@@ -161,6 +160,14 @@ interface KeyEntry {
161
160
  value: Key | null
162
161
  }
163
162
 
164
- const ERR_INVALID_TOKEN = new Err('INVALID_TOKEN')
165
- const ERR_INVALID_KEY = new Err('INVALID_KEY')
166
- const ERR_FORGED_KEY = new Err('FORGED_KEY')
163
+ const ERR_INVALID_TOKEN = new (class InvalidTokenError extends Error {
164
+ public readonly code = 'INVALID_TOKEN'
165
+ })()
166
+
167
+ const ERR_INVALID_KEY = new (class InvalidKeyError extends Error {
168
+ public readonly code = 'INVALID_KEY'
169
+ })()
170
+
171
+ const ERR_FORGED_KEY = new (class ForgedKeyError extends Error {
172
+ public readonly code = 'FORGED_KEY'
173
+ })()
@@ -4,6 +4,7 @@ import { timeout } from '@toa.io/generic'
4
4
  import { Effect as Encrypt } from './encrypt'
5
5
  import { Computation as Decrypt } from './decrypt'
6
6
  import { type Context, type Identity } from './lib'
7
+ import type { Secret } from '@toa.io/types'
7
8
 
8
9
  let encrypt: Encrypt
9
10
  let decrypt: Decrypt
@@ -18,8 +19,8 @@ const authority = generate()
18
19
  beforeEach(() => {
19
20
  context.configuration = {
20
21
  keys: [
21
- { id: 'key0', key: 'sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs' },
22
- { id: 'legacy0', key: 'k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog', format: 'paseto' }
22
+ { id: 'key0', key: secret('sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs') },
23
+ { id: 'legacy0', key: secret('k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog'), format: 'paseto' }
23
24
  ],
24
25
  lifetime: 1,
25
26
  refresh: 2,
@@ -39,12 +40,12 @@ beforeEach(() => {
39
40
  it('should use the first encryption key as active and expose its id as kid', async () => {
40
41
  context.configuration.keys.unshift({
41
42
  id: 'legacy-first',
42
- key: 'k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog',
43
+ key: secret('k3.local.m28p8SrbS467t-2IUjQuSOqmjvi24TbXhyjAW_dOrog'),
43
44
  format: 'paseto'
44
45
  })
45
46
  context.configuration.keys.push({
46
47
  id: 'key1',
47
- key: '5I0iSKw3yfBkQ4AXfA8eR-tWR0Q1dpn4x3bPrPzHkP0'
48
+ key: secret('5I0iSKw3yfBkQ4AXfA8eR-tWR0Q1dpn4x3bPrPzHkP0')
48
49
  })
49
50
 
50
51
  encrypt.mount(context)
@@ -119,3 +120,7 @@ it('should encrypt without lifetime INSECURE', async () => {
119
120
 
120
121
  expect(decrypted.identity).toMatchObject(identity)
121
122
  })
123
+
124
+ function secret (value: string): Secret {
125
+ return { unwrap: () => value }
126
+ }
@@ -1,4 +1,3 @@
1
- import { Err } from 'error-value'
2
1
  import { jweKey } from './lib'
3
2
  import { load } from './lib/jose'
4
3
  import type { Operation, Maybe } from '@toa.io/types'
@@ -14,7 +13,7 @@ export class Effect implements Operation {
14
13
  if (key === undefined)
15
14
  throw new TypeError('At least one JWE key must be configured')
16
15
 
17
- this.key = key
16
+ this.key = { id: key.id, key: key.key.unwrap() }
18
17
  this.lifetime = context.configuration.lifetime * 1000
19
18
  }
20
19
 
@@ -52,4 +51,6 @@ function within (scope: string, roles: string[]): boolean {
52
51
  return roles.some((role) => role === scope || scope.startsWith(role + ':'))
53
52
  }
54
53
 
55
- const ERR_INACCESSIBLE_SCOPE = new Err('INACCESSIBLE_SCOPE')
54
+ const ERR_INACCESSIBLE_SCOPE = new (class InaccessibleScopeError extends Error {
55
+ public readonly code = 'INACCESSIBLE_SCOPE'
56
+ })()
@@ -1,4 +1,4 @@
1
- import type { Call, Maybe, Observation, telemetry } from '@toa.io/types'
1
+ import type { Call, Maybe, Observation, Secret, telemetry } from '@toa.io/types'
2
2
 
3
3
  export interface Context {
4
4
  local: {
@@ -31,7 +31,8 @@ export interface Configuration {
31
31
  }
32
32
  }
33
33
 
34
- export interface ConfiguredKey extends Pick<Key, 'id' | 'key'> {
34
+ export interface ConfiguredKey extends Pick<Key, 'id'> {
35
+ key: Secret
35
36
  format?: 'jwe' | 'paseto'
36
37
  }
37
38
 
package/context.toa.yaml CHANGED
@@ -10,9 +10,9 @@ configuration:
10
10
  identity.tokens:
11
11
  keys:
12
12
  - id: key0
13
- key: sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs
13
+ key: $IDENTITY_TOKENS_ENCRYPTION_KEY0
14
14
  - id: legacy0
15
- key: k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA
15
+ key: $IDENTITY_TOKENS_KEY0
16
16
  format: paseto
17
17
 
18
18
  identity.federation:
@@ -23,7 +23,7 @@ and pepper.
23
23
  configuration:
24
24
  identity.basic:
25
25
  rounds: 10 # salt rounds
26
- pepper: '' # hashing pepper
26
+ pepper: $IDENTITY_BASIC_PEPPER # hashing pepper, a secret
27
27
  ```
28
28
 
29
29
  ### Credentials constraints
@@ -181,7 +181,10 @@ name: users
181
181
 
182
182
  entity:
183
183
  schema:
184
- name: string
184
+ type: object
185
+ properties:
186
+ name:
187
+ type: string
185
188
 
186
189
  exposition:
187
190
  /:
@@ -0,0 +1,207 @@
1
+ @security
2
+ Feature: Configuration values
3
+
4
+ The values component declares how it is exposed in its own manifest, and that manifest
5
+ is what this mounts. Reading takes `system:configuration:get`, creating takes
6
+ `system:configuration:create`, and the one who creates is recorded as the originator.
7
+
8
+ Background:
9
+ # developer:secret, user:12345
10
+ Given the `identity.basic` database contains:
11
+ | _id | authority | username | password |
12
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
13
+ | e8e4f9c2a68d419b861403d71fabc915 | nex | user | $2b$10$Frszmrmsz9iwSXzBbRRMKeDVKsNxozkrLNSsN.SnVC.KPxLtQr/bK |
14
+ And the `identity.roles` database contains:
15
+ | _id | identity | role |
16
+ | 1c0b2a3d4e5f4a6b8c7d9e0f1a2b3c4d | efe3a65ebbee47ed95a73edd911ea328 | system:configuration |
17
+ | 2c0b2a3d4e5f4a6b8c7d9e0f1a2b3c4d | e8e4f9c2a68d419b861403d71fabc915 | system:configuration:get |
18
+ And the `identity.bans` database is empty
19
+ And the `configuration.values` database is empty
20
+ And the configuration values are deployed:
21
+ """yaml
22
+ dummies.dummy:
23
+ epoch: e1
24
+ schema:
25
+ type: object
26
+ properties:
27
+ foo:
28
+ type: string
29
+ bar:
30
+ type: string
31
+ default: world
32
+ defaults:
33
+ foo: deployed
34
+ """
35
+ And the configuration values are running
36
+
37
+ Scenario: Reading the deployed configuration
38
+ When the following request is received:
39
+ """
40
+ GET /configuration/values/dummies.dummy/ HTTP/1.1
41
+ host: nex.toa.io
42
+ authorization: Basic dXNlcjoxMjM0NQ==
43
+ accept: application/yaml
44
+ """
45
+ Then the following reply is sent:
46
+ """
47
+ 200 OK
48
+ content-type: application/yaml
49
+
50
+ configuration:
51
+ foo: deployed
52
+ schema:
53
+ type: object
54
+ properties:
55
+ foo:
56
+ type: string
57
+ bar:
58
+ type: string
59
+ default: world
60
+ epoch: e1
61
+ """
62
+
63
+ Scenario: Listing the configurations
64
+ When the following request is received:
65
+ """
66
+ GET /configuration/values/ HTTP/1.1
67
+ host: nex.toa.io
68
+ authorization: Basic dXNlcjoxMjM0NQ==
69
+ accept: application/yaml
70
+ """
71
+ Then the following reply is sent:
72
+ """
73
+ 200 OK
74
+ content-type: application/yaml
75
+
76
+ - component: dummies.dummy
77
+ epoch: e1
78
+ schema:
79
+ type: object
80
+ properties:
81
+ foo:
82
+ type: string
83
+ bar:
84
+ type: string
85
+ default: world
86
+ configuration:
87
+ foo: deployed
88
+ """
89
+
90
+ Scenario: Listing without the role
91
+ When the following request is received:
92
+ """
93
+ GET /configuration/values/ HTTP/1.1
94
+ host: nex.toa.io
95
+ """
96
+ Then the following reply is sent:
97
+ """
98
+ 401 Unauthorized
99
+ """
100
+
101
+ Scenario: Reading without the role
102
+ When the following request is received:
103
+ """
104
+ GET /configuration/values/dummies.dummy/ HTTP/1.1
105
+ host: nex.toa.io
106
+ """
107
+ Then the following reply is sent:
108
+ """
109
+ 401 Unauthorized
110
+ """
111
+
112
+ Scenario: Reading the configuration of an unknown component
113
+ When the following request is received:
114
+ """
115
+ GET /configuration/values/dummies.unknown/ HTTP/1.1
116
+ host: nex.toa.io
117
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
118
+ """
119
+ Then the following reply is sent:
120
+ """
121
+ 404 Not Found
122
+ """
123
+
124
+ Scenario: Creating configuration
125
+ When the following request is received:
126
+ """
127
+ POST /configuration/values/dummies.dummy/ HTTP/1.1
128
+ host: nex.toa.io
129
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
130
+ accept: application/yaml
131
+ content-type: application/yaml
132
+
133
+ configuration:
134
+ foo: created
135
+ """
136
+ Then the following reply is sent:
137
+ """
138
+ 201 Created
139
+ content-type: application/yaml
140
+
141
+ epoch: e1
142
+ """
143
+ When the following request is received:
144
+ """
145
+ GET /configuration/values/dummies.dummy/ HTTP/1.1
146
+ host: nex.toa.io
147
+ authorization: Basic dXNlcjoxMjM0NQ==
148
+ accept: application/yaml
149
+ """
150
+ Then the following reply is sent:
151
+ """
152
+ 200 OK
153
+ content-type: application/yaml
154
+
155
+ configuration:
156
+ foo: created
157
+ bar: world
158
+ epoch: e1
159
+ """
160
+
161
+ Scenario: Creating configuration without the role
162
+ When the following request is received:
163
+ """
164
+ POST /configuration/values/dummies.dummy/ HTTP/1.1
165
+ host: nex.toa.io
166
+ authorization: Basic dXNlcjoxMjM0NQ==
167
+ content-type: application/yaml
168
+
169
+ configuration:
170
+ foo: created
171
+ """
172
+ Then the following reply is sent:
173
+ """
174
+ 403 Forbidden
175
+ """
176
+
177
+ Scenario: Creating configuration not fitting the schema
178
+ When the following request is received:
179
+ """
180
+ POST /configuration/values/dummies.dummy/ HTTP/1.1
181
+ host: nex.toa.io
182
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
183
+ content-type: application/yaml
184
+
185
+ configuration:
186
+ foo:
187
+ nested: true
188
+ """
189
+ Then the following reply is sent:
190
+ """
191
+ 422 Unprocessable Entity
192
+ """
193
+
194
+ Scenario: Creating configuration for an unknown component
195
+ When the following request is received:
196
+ """
197
+ POST /configuration/values/dummies.unknown/ HTTP/1.1
198
+ host: nex.toa.io
199
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
200
+ content-type: application/yaml
201
+
202
+ configuration: {}
203
+ """
204
+ Then the following reply is sent:
205
+ """
206
+ 422 Unprocessable Entity
207
+ """
@@ -20,7 +20,7 @@ Feature: CORS Support
20
20
  """
21
21
  204 No Content
22
22
  access-control-allow-credentials: true
23
- access-control-allow-headers: accept, authorization, content-type, if-match, if-none-match, origin
23
+ access-control-allow-headers: accept, authorization, content-type, if-match, if-none-match
24
24
  access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, LOCK, UNLOCK
25
25
  access-control-allow-origin: https://hello.world
26
26
  access-control-max-age: 3600
@@ -74,7 +74,7 @@ Feature: Dev
74
74
  Then the following reply is sent:
75
75
  """
76
76
  204 No Content
77
- access-control-allow-headers: accept, authorization, content-type, if-match, if-none-match, faulty
77
+ access-control-allow-headers: accept, authorization, content-type, faulty, if-match, if-none-match
78
78
  """
79
79
  When the following request is received:
80
80
  """
@@ -195,13 +195,14 @@ Feature: Identity Federation
195
195
 
196
196
  Then after 0.2 seconds
197
197
 
198
- # check the role
198
+ # the role is checked over the id token: a Token carries the roles held when it
199
+ # was issued, and this one was issued before the grant
199
200
  When the following request is received:
200
201
  """
201
202
  GET /identity/ HTTP/1.1
202
203
  host: nex.toa.io
203
204
  accept: application/yaml
204
- authorization: Token ${{ root.token }}
205
+ authorization: Bearer ${{ root.id_token }}
205
206
  """
206
207
  Then the following reply is sent:
207
208
  """
@@ -334,7 +335,11 @@ Feature: Identity Federation
334
335
  trust:
335
336
  - iss: http://localhost:44444
336
337
  aud: nex
337
- secret: secret
338
+ secret: $IDP_SECRET
339
+ """
340
+ And the configuration secrets:
341
+ """yaml
342
+ IDP_SECRET: secret
338
343
  """
339
344
  And auth code for Alice is issued for https://web.toa.io/callback/
340
345
  When the following request is received:
@@ -361,12 +366,16 @@ Feature: Identity Federation
361
366
  signature:
362
367
  iss: io.toa.nex.id
363
368
  kid: key-id
364
- key: |
365
- -----BEGIN PRIVATE KEY-----
366
- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg9x9DatwH0GZHSCo9
367
- TMHTVXyeY0YQ8qb73jHV2v4MsyehRANCAAQwaYlna2hSV3G/RIlNLV41lg8Pm4Kf
368
- HfCuKKiw3BIJTnSArAMJLSy1vYwSIMHz720mkmWTrWuQkkjvkDpZxfRv
369
- -----END PRIVATE KEY-----
369
+ key: $IDP_KEY
370
+ """
371
+ And the configuration secrets:
372
+ """yaml
373
+ IDP_KEY: |
374
+ -----BEGIN PRIVATE KEY-----
375
+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg9x9DatwH0GZHSCo9
376
+ TMHTVXyeY0YQ8qb73jHV2v4MsyehRANCAAQwaYlna2hSV3G/RIlNLV41lg8Pm4Kf
377
+ HfCuKKiw3BIJTnSArAMJLSy1vYwSIMHz720mkmWTrWuQkkjvkDpZxfRv
378
+ -----END PRIVATE KEY-----
370
379
  """
371
380
  And auth code for Bob is issued for https://web.toa.io/callback/
372
381
  When the following request is received:
@@ -76,6 +76,45 @@ Feature: Custom tokens
76
76
  200 OK
77
77
  """
78
78
 
79
+ Scenario: Issuing a token for another identity
80
+ Given the `identity.basic` database contains:
81
+ | _id | authority | username | password |
82
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
83
+ | e8e4f9c2a68d419b861403d71fabc915 | nex | user | $2b$10$Frszmrmsz9iwSXzBbRRMKeDVKsNxozkrLNSsN.SnVC.KPxLtQr/bK |
84
+ And the `identity.roles` database contains:
85
+ | _id | identity | role |
86
+ | 9c4702490ff84f2a9e1b1da2ab64bdd4 | efe3a65ebbee47ed95a73edd911ea328 | system:identity:tokens |
87
+ When the following request is received:
88
+ """
89
+ POST /identity/tokens/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
90
+ host: nex.toa.io
91
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
92
+ content-type: application/yaml
93
+ accept: application/yaml
94
+
95
+ label: On behalf
96
+ lifetime: 600
97
+ """
98
+ Then the following reply is sent:
99
+ """
100
+ 201 Created
101
+
102
+ token: ${{ token }}
103
+ """
104
+ When the following request is received:
105
+ """
106
+ GET /identity/ HTTP/1.1
107
+ host: nex.toa.io
108
+ authorization: Token ${{ token }}
109
+ accept: application/yaml
110
+ """
111
+ Then the following reply is sent:
112
+ """
113
+ 200 OK
114
+
115
+ id: e8e4f9c2a68d419b861403d71fabc915
116
+ """
117
+
79
118
  Scenario: Token with restricted scopes
80
119
  When the following request is received:
81
120
  """
@@ -90,3 +90,19 @@ Feature: Reading the introspection map
90
90
  """
91
91
  403 Forbidden
92
92
  """
93
+
94
+ Scenario: The map does not take the application's root directives
95
+ Given the annotation of the introspection map under:
96
+ """yaml
97
+ /:
98
+ auth:anonymous: true
99
+ """
100
+ When the following request is received:
101
+ """
102
+ GET /introspection/nodes/ HTTP/1.1
103
+ host: nex.toa.io
104
+ """
105
+ Then the following reply is sent:
106
+ """
107
+ 401 Unauthorized
108
+ """
@@ -84,7 +84,7 @@ Feature: HTTP context mapping
84
84
  Then the following reply is sent:
85
85
  """
86
86
  204 No Content
87
- access-control-allow-headers: accept, accept-language, authorization, content-type, foo, if-match, if-none-match, origin
87
+ access-control-allow-headers: accept, accept-language, authorization, content-type, foo, if-match, if-none-match
88
88
  """
89
89
 
90
90
  Scenario: Mapping the value of an arbitrary header
@@ -186,5 +186,5 @@ Feature: Download external resources
186
186
  Then the following reply is sent:
187
187
  """
188
188
  204 No Content
189
- access-control-allow-headers: accept, authorization, content-attributes, content-location, content-type, if-match, if-none-match, origin
189
+ access-control-allow-headers: accept, authorization, content-attributes, content-location, content-type, if-match, if-none-match
190
190
  """
@@ -111,6 +111,6 @@ Feature: Octets metadata
111
111
  Then the following reply is sent:
112
112
  """
113
113
  204 No Content
114
- access-control-allow-headers: accept, authorization, content-attributes, content-location, content-type, if-match, if-none-match, origin
114
+ access-control-allow-headers: accept, authorization, content-attributes, content-location, content-type, if-match, if-none-match
115
115
  access-control-allow-origin: https://example.com
116
116
  """
@@ -20,16 +20,16 @@ Feature: Web Authentication
20
20
  201 Created
21
21
 
22
22
  challenge: ${{ challenge }}
23
- identity: ${{ identity.id }}
24
23
  timeout: 60000
25
- authenticatorSelection:
26
- requireResidentKey: true
27
- residentKey: required
24
+ identity: ${{ identity.id }}
28
25
  pubKeyCredParams:
29
26
  - type: public-key
30
27
  alg: -7
31
28
  - type: public-key
32
29
  alg: -257
30
+ authenticatorSelection:
31
+ residentKey: required
32
+ requireResidentKey: true
33
33
  """
34
34
 
35
35
  Scenario: Create a challenge to create a passkey to new identity
@@ -53,14 +53,14 @@ Feature: Web Authentication
53
53
  authorization: Token ${{ token }}
54
54
 
55
55
  challenge: ${{ challenge }}
56
- identity: ${{ identity }}
57
56
  timeout: 60000
58
- authenticatorSelection:
59
- requireResidentKey: true
60
- residentKey: required
57
+ identity: ${{ identity }}
61
58
  pubKeyCredParams:
62
59
  - type: public-key
63
60
  alg: -7
64
61
  - type: public-key
65
62
  alg: -257
63
+ authenticatorSelection:
64
+ residentKey: required
65
+ requireResidentKey: true
66
66
  """