@toa.io/extensions.exposition 1.0.0-alpha.49 → 1.0.0-alpha.50
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/components/identity.federation/manifest.toa.yaml +4 -0
- package/components/identity.federation/operations/authenticate.js +2 -2
- package/components/identity.federation/operations/authenticate.js.map +1 -1
- package/components/identity.federation/operations/lib/jwt.js +10 -5
- package/components/identity.federation/operations/lib/jwt.js.map +1 -1
- package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/operations/types/context.d.ts +2 -1
- package/components/identity.federation/source/authenticate.ts +2 -2
- package/components/identity.federation/source/lib/jwt.test.ts +48 -2
- package/components/identity.federation/source/lib/jwt.ts +14 -5
- package/components/identity.federation/source/types/context.ts +2 -1
- package/documentation/access.md +52 -26
- package/documentation/authorities.md +3 -7
- package/documentation/tree.md +13 -0
- package/features/auth.claim.feature +170 -0
- package/features/authorities.feature +3 -3
- package/features/authorities.federation.feature +4 -3
- package/features/authorities.tokens.feature +1 -2
- package/features/identity.federation.feature +2 -2
- package/features/routes.feature +63 -0
- package/features/steps/IdP.ts +2 -2
- package/features/steps/components/echo.beacon/manifest.toa.yaml +2 -0
- package/features/steps/components/echo.beacon/operations/hello.js +5 -0
- package/features/vary.feature +1 -2
- package/package.json +7 -7
- package/schemas/node.cos.yaml +1 -0
- package/source/Gateway.ts +28 -7
- package/source/HTTP/Server.ts +2 -7
- package/source/RTD/Node.ts +8 -1
- package/source/RTD/Route.ts +1 -1
- package/source/RTD/factory.ts +4 -1
- package/source/RTD/syntax/parse.ts +37 -24
- package/source/RTD/syntax/types.ts +1 -0
- package/source/directives/auth/Authorization.ts +5 -2
- package/source/directives/auth/Federation.ts +84 -0
- package/transpiled/Gateway.d.ts +1 -0
- package/transpiled/Gateway.js +20 -4
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Server.js +2 -5
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/RTD/Node.d.ts +2 -0
- package/transpiled/RTD/Node.js +7 -1
- package/transpiled/RTD/Node.js.map +1 -1
- package/transpiled/RTD/Route.d.ts +1 -1
- package/transpiled/RTD/Route.js.map +1 -1
- package/transpiled/RTD/factory.js +4 -1
- package/transpiled/RTD/factory.js.map +1 -1
- package/transpiled/RTD/syntax/parse.js +34 -22
- package/transpiled/RTD/syntax/parse.js.map +1 -1
- package/transpiled/RTD/syntax/types.d.ts +1 -0
- package/transpiled/RTD/syntax/types.js.map +1 -1
- package/transpiled/directives/auth/Authorization.js +3 -1
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/Federation.d.ts +16 -0
- package/transpiled/directives/auth/Federation.js +57 -0
- package/transpiled/directives/auth/Federation.js.map +1 -0
- package/transpiled/tsconfig.tsbuildinfo +1 -1
package/documentation/access.md
CHANGED
|
@@ -37,11 +37,8 @@ the directive's value.
|
|
|
37
37
|
Given the Route declaration and corresponding HTTP request:
|
|
38
38
|
|
|
39
39
|
```yaml
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
exposition:
|
|
43
|
-
/users/:user-id:
|
|
44
|
-
id: "user-id"
|
|
40
|
+
/users/:user-id:
|
|
41
|
+
id: "user-id"
|
|
45
42
|
```
|
|
46
43
|
|
|
47
44
|
```http
|
|
@@ -57,11 +54,8 @@ is `87480f2bd88048518c529d7957475ecd`.
|
|
|
57
54
|
Grants access if resolved Identity has a role matching the directive's value or one of its values.
|
|
58
55
|
|
|
59
56
|
```yaml
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
exposition:
|
|
63
|
-
/code:
|
|
64
|
-
role: [developer, reviewer]
|
|
57
|
+
/code:
|
|
58
|
+
role: [developer, reviewer]
|
|
65
59
|
```
|
|
66
60
|
|
|
67
61
|
Access will be granted if the resolved Identity has a role that matches `developer` or `reviewer`.
|
|
@@ -73,11 +67,49 @@ Read [Roles](#roles) section for more details.
|
|
|
73
67
|
The `role` directive can be used with a placeholder in the route.
|
|
74
68
|
|
|
75
69
|
```yaml
|
|
76
|
-
|
|
70
|
+
/:org-id:
|
|
71
|
+
role: app:{org-id}:moderator
|
|
72
|
+
```
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
### `claim`
|
|
75
|
+
|
|
76
|
+
Grants access if `Bearer` authentication scheme is used and the claim's property matches specified.
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
/:
|
|
80
|
+
auth:claim:
|
|
81
|
+
iss: https://id.example.com
|
|
82
|
+
sub: someone
|
|
83
|
+
aud: stars
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> If OIDC token claim contains `aud`
|
|
87
|
+
> as [an array](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation), the
|
|
88
|
+
> directive will match if at least one value.
|
|
89
|
+
|
|
90
|
+
At least one property is required.
|
|
91
|
+
|
|
92
|
+
Values may refer to the Route parameters, or a request authority:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
/secrets/:org-id:
|
|
96
|
+
auth:claim:
|
|
97
|
+
iss: https://id.org.com
|
|
98
|
+
sub: /:org-id
|
|
99
|
+
aud: :authority
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
An expression `:domain` will match if the domain in the value of `iss` matches the request
|
|
103
|
+
authority, excluding the most specific subdomain.
|
|
104
|
+
|
|
105
|
+
Issuer `https://accounts.example.com` matches request authorities `images.example.com`
|
|
106
|
+
and `sub.images.example.com`, but not `images.another.com`.
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
/images/:user-id:
|
|
110
|
+
auth:claim:
|
|
111
|
+
iss: :domain
|
|
112
|
+
sub: /:org-id
|
|
81
113
|
```
|
|
82
114
|
|
|
83
115
|
### `rule`
|
|
@@ -88,13 +120,10 @@ directives grant access. The value of the `rule` directive can be a single Rule
|
|
|
88
120
|
#### Example
|
|
89
121
|
|
|
90
122
|
```yaml
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
rule:
|
|
96
|
-
id: user-id
|
|
97
|
-
role: developer
|
|
123
|
+
/commits/:user-id:
|
|
124
|
+
rule:
|
|
125
|
+
id: user-id
|
|
126
|
+
role: developer
|
|
98
127
|
```
|
|
99
128
|
|
|
100
129
|
Access will be granted if an Identity matches a `user-id` placeholder and has a Role of `developer`.
|
|
@@ -124,11 +153,8 @@ directive.
|
|
|
124
153
|
#### Example
|
|
125
154
|
|
|
126
155
|
```yaml
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
/exposition:
|
|
130
|
-
/commits/:user-id:
|
|
131
|
-
role: developer:senior
|
|
156
|
+
/commits/:user-id:
|
|
157
|
+
role: developer:senior
|
|
132
158
|
```
|
|
133
159
|
|
|
134
160
|
The example above defines a `role` directive with the specified `developer:senior` Role Scope.
|
|
@@ -17,13 +17,6 @@ exposition:
|
|
|
17
17
|
two: the.two.com
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Ingress
|
|
21
|
-
|
|
22
|
-
Each host in the authority definition is used to create a Kubernetes Ingress resource.
|
|
23
|
-
|
|
24
|
-
> If the application is accessed with the `:authority` that does not match the authority definition,
|
|
25
|
-
> the response with `404` status code is returned.
|
|
26
|
-
|
|
27
20
|
## Embedding
|
|
28
21
|
|
|
29
22
|
To pass the requested authority to the operation call, [`vary:embed` directive](vary.md#embeddings)
|
|
@@ -40,6 +33,9 @@ exposition:
|
|
|
40
33
|
endpoint: observe
|
|
41
34
|
```
|
|
42
35
|
|
|
36
|
+
If the value of the `authority` pseudo-header is not present in the `authorities` definition,
|
|
37
|
+
then the value of the `authority` pseudo-header is embedded as is.
|
|
38
|
+
|
|
43
39
|
## Identity
|
|
44
40
|
|
|
45
41
|
Credentials stored or issued by the [authentication system](identity.md) are associated with an
|
package/documentation/tree.md
CHANGED
|
@@ -56,6 +56,19 @@ as it provides a more specific match compared to the generic `/users/:id` route.
|
|
|
56
56
|
|
|
57
57
|
The priority of Routes with the same specificity is determined by the order of declaration.
|
|
58
58
|
|
|
59
|
+
## Route forwarding
|
|
60
|
+
|
|
61
|
+
A Route can be forwarded to another Route by specifying the destination Route as the value of the
|
|
62
|
+
Route.
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
/destination/:var: ...
|
|
66
|
+
/static: /destination/hello
|
|
67
|
+
/variables/:bar: /destination/:bar
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Forwarding Route variables are mapped to the forwarded Route variables if they have the same name.
|
|
71
|
+
|
|
59
72
|
## Methods
|
|
60
73
|
|
|
61
74
|
Methods are mappings of the HTTP methods to the corresponding operations.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
@security
|
|
2
|
+
Feature: Federated identity authentication
|
|
3
|
+
|
|
4
|
+
Background:
|
|
5
|
+
Given the `identity.federation` database is empty
|
|
6
|
+
And local IDP is running
|
|
7
|
+
And the IDP token for Bob is issued
|
|
8
|
+
And the `identity.federation` configuration:
|
|
9
|
+
"""yaml
|
|
10
|
+
trust:
|
|
11
|
+
- iss: http://localhost:44444
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
Scenario: Full claim
|
|
15
|
+
Given the annotation:
|
|
16
|
+
"""yaml
|
|
17
|
+
/:
|
|
18
|
+
GET:
|
|
19
|
+
auth:claim:
|
|
20
|
+
iss: http://localhost:44444
|
|
21
|
+
aud: test
|
|
22
|
+
sub: Bob
|
|
23
|
+
dev:stub: ok
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
When the following request is received:
|
|
27
|
+
"""
|
|
28
|
+
GET / HTTP/1.1
|
|
29
|
+
host: nex.toa.io
|
|
30
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
31
|
+
"""
|
|
32
|
+
Then the following reply is sent:
|
|
33
|
+
"""
|
|
34
|
+
200 OK
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
Scenario: Only `sub`
|
|
38
|
+
Given the annotation:
|
|
39
|
+
"""yaml
|
|
40
|
+
/:
|
|
41
|
+
GET:
|
|
42
|
+
auth:claim:
|
|
43
|
+
sub: Bob
|
|
44
|
+
dev:stub: ok
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
When the following request is received:
|
|
48
|
+
"""
|
|
49
|
+
GET / HTTP/1.1
|
|
50
|
+
host: nex.toa.io
|
|
51
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
52
|
+
"""
|
|
53
|
+
Then the following reply is sent:
|
|
54
|
+
"""
|
|
55
|
+
200 OK
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
Scenario: No `sub`
|
|
59
|
+
Given the annotation:
|
|
60
|
+
"""yaml
|
|
61
|
+
/:
|
|
62
|
+
GET:
|
|
63
|
+
auth:claim:
|
|
64
|
+
iss: http://localhost:44444
|
|
65
|
+
aud: test
|
|
66
|
+
dev:stub: ok
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
When the following request is received:
|
|
70
|
+
"""
|
|
71
|
+
GET / HTTP/1.1
|
|
72
|
+
host: nex.toa.io
|
|
73
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
74
|
+
"""
|
|
75
|
+
Then the following reply is sent:
|
|
76
|
+
"""
|
|
77
|
+
200 OK
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
Scenario: `sub` mismatch
|
|
81
|
+
Given the annotation:
|
|
82
|
+
"""yaml
|
|
83
|
+
/:
|
|
84
|
+
GET:
|
|
85
|
+
auth:claim:
|
|
86
|
+
iss: http://localhost:44444
|
|
87
|
+
sub: Alice
|
|
88
|
+
dev:stub: ok
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
When the following request is received:
|
|
92
|
+
"""
|
|
93
|
+
GET / HTTP/1.1
|
|
94
|
+
host: nex.toa.io
|
|
95
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
96
|
+
"""
|
|
97
|
+
Then the following reply is sent:
|
|
98
|
+
"""
|
|
99
|
+
403 Forbidden
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
Scenario: `aud` mismatch
|
|
103
|
+
Given the annotation:
|
|
104
|
+
"""yaml
|
|
105
|
+
/:
|
|
106
|
+
GET:
|
|
107
|
+
auth:claim:
|
|
108
|
+
iss: http://localhost:44444
|
|
109
|
+
aud: goalkeepers
|
|
110
|
+
dev:stub: ok
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
When the following request is received:
|
|
114
|
+
"""
|
|
115
|
+
GET / HTTP/1.1
|
|
116
|
+
host: nex.toa.io
|
|
117
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
118
|
+
"""
|
|
119
|
+
Then the following reply is sent:
|
|
120
|
+
"""
|
|
121
|
+
403 Forbidden
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
Scenario: Matching authority and Route parameter
|
|
125
|
+
Given the annotation:
|
|
126
|
+
"""yaml
|
|
127
|
+
authorities:
|
|
128
|
+
test: the.test.local
|
|
129
|
+
/:
|
|
130
|
+
/:id:
|
|
131
|
+
GET:
|
|
132
|
+
auth:claim:
|
|
133
|
+
aud: :authority
|
|
134
|
+
sub: /:id
|
|
135
|
+
dev:stub: ok
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
When the following request is received:
|
|
139
|
+
"""
|
|
140
|
+
GET /Bob/ HTTP/1.1
|
|
141
|
+
host: the.test.local
|
|
142
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
143
|
+
"""
|
|
144
|
+
Then the following reply is sent:
|
|
145
|
+
"""
|
|
146
|
+
200 OK
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
Scenario: `iss` matching authority common domain
|
|
150
|
+
Given the annotation:
|
|
151
|
+
"""yaml
|
|
152
|
+
/:
|
|
153
|
+
/:id:
|
|
154
|
+
GET:
|
|
155
|
+
auth:claim:
|
|
156
|
+
iss: :domain
|
|
157
|
+
sub: /:id
|
|
158
|
+
dev:stub: ok
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
When the following request is received:
|
|
162
|
+
"""
|
|
163
|
+
GET /Bob/ HTTP/1.1
|
|
164
|
+
host: localhost
|
|
165
|
+
authorization: Bearer ${{ Bob.id_token }}
|
|
166
|
+
"""
|
|
167
|
+
Then the following reply is sent:
|
|
168
|
+
"""
|
|
169
|
+
200 OK
|
|
170
|
+
"""
|
|
@@ -19,6 +19,8 @@ Feature: Authorities
|
|
|
19
19
|
"""
|
|
20
20
|
200 OK
|
|
21
21
|
"""
|
|
22
|
+
|
|
23
|
+
# arbitrary authorities are also allowed
|
|
22
24
|
When the following request is received:
|
|
23
25
|
"""
|
|
24
26
|
GET / HTTP/1.1
|
|
@@ -26,7 +28,5 @@ Feature: Authorities
|
|
|
26
28
|
"""
|
|
27
29
|
Then the following reply is sent:
|
|
28
30
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Unknown authority
|
|
31
|
+
200 OK
|
|
32
32
|
"""
|
|
@@ -5,7 +5,6 @@ Feature: OIDC tokens with authorities
|
|
|
5
5
|
"""yaml
|
|
6
6
|
authorities:
|
|
7
7
|
one: the.one.com
|
|
8
|
-
two: the.two.com
|
|
9
8
|
/:
|
|
10
9
|
/:id:
|
|
11
10
|
auth:id: id
|
|
@@ -65,6 +64,8 @@ Feature: OIDC tokens with authorities
|
|
|
65
64
|
"""
|
|
66
65
|
200 OK
|
|
67
66
|
"""
|
|
67
|
+
|
|
68
|
+
# authorization will create new identity within `one` authority
|
|
68
69
|
When the following request is received:
|
|
69
70
|
"""
|
|
70
71
|
GET /${{ Two.id }}/ HTTP/1.1
|
|
@@ -73,7 +74,7 @@ Feature: OIDC tokens with authorities
|
|
|
73
74
|
"""
|
|
74
75
|
Then the following reply is sent:
|
|
75
76
|
"""
|
|
76
|
-
|
|
77
|
+
403 Forbidden
|
|
77
78
|
"""
|
|
78
79
|
|
|
79
80
|
# access `two` authority
|
|
@@ -85,7 +86,7 @@ Feature: OIDC tokens with authorities
|
|
|
85
86
|
"""
|
|
86
87
|
Then the following reply is sent:
|
|
87
88
|
"""
|
|
88
|
-
|
|
89
|
+
403 Forbidden
|
|
89
90
|
"""
|
|
90
91
|
When the following request is received:
|
|
91
92
|
"""
|
|
@@ -5,7 +5,6 @@ Feature: Token credentials with authorities
|
|
|
5
5
|
"""yaml
|
|
6
6
|
authorities:
|
|
7
7
|
one: the.one.com
|
|
8
|
-
two: the.two.com
|
|
9
8
|
/:
|
|
10
9
|
/:id:
|
|
11
10
|
auth:id: id
|
|
@@ -43,7 +42,7 @@ Feature: Token credentials with authorities
|
|
|
43
42
|
id: ${{ one.id }}
|
|
44
43
|
"""
|
|
45
44
|
|
|
46
|
-
# create identity within the `two` authority
|
|
45
|
+
# create identity within the `the.two.com` authority
|
|
47
46
|
When the following request is received:
|
|
48
47
|
"""
|
|
49
48
|
POST /identity/basic/ HTTP/1.1
|
|
@@ -3,7 +3,7 @@ Feature: Identity Federation
|
|
|
3
3
|
|
|
4
4
|
Background:
|
|
5
5
|
Given the `identity.federation` database is empty
|
|
6
|
-
|
|
6
|
+
And local IDP is running
|
|
7
7
|
|
|
8
8
|
Scenario: Getting identity for a new user
|
|
9
9
|
Given the `identity.federation` configuration:
|
|
@@ -169,7 +169,7 @@ Feature: Identity Federation
|
|
|
169
169
|
- iss: http://localhost:44444
|
|
170
170
|
principal:
|
|
171
171
|
iss: http://localhost:44444
|
|
172
|
-
sub: root
|
|
172
|
+
sub: root
|
|
173
173
|
"""
|
|
174
174
|
And the IDP token for root is issued
|
|
175
175
|
|
package/features/routes.feature
CHANGED
|
@@ -138,6 +138,32 @@ Feature: Routes
|
|
|
138
138
|
201 Created
|
|
139
139
|
"""
|
|
140
140
|
|
|
141
|
+
Scenario: Routes with default namespace conflicts
|
|
142
|
+
Given the `echo` is running with the following manifest:
|
|
143
|
+
"""yaml
|
|
144
|
+
exposition:
|
|
145
|
+
/:foo:
|
|
146
|
+
io:output: true
|
|
147
|
+
PUT: compute
|
|
148
|
+
"""
|
|
149
|
+
And the `echo.beacon` is running with the following manifest:
|
|
150
|
+
"""yaml
|
|
151
|
+
exposition:
|
|
152
|
+
/:
|
|
153
|
+
io:output: true
|
|
154
|
+
GET: hello
|
|
155
|
+
"""
|
|
156
|
+
When the following request is received:
|
|
157
|
+
"""
|
|
158
|
+
GET /echo/beacon/ HTTP/1.1
|
|
159
|
+
host: nex.toa.io
|
|
160
|
+
accept: application/yaml
|
|
161
|
+
"""
|
|
162
|
+
Then the following reply is sent:
|
|
163
|
+
"""
|
|
164
|
+
200 OK
|
|
165
|
+
"""
|
|
166
|
+
|
|
141
167
|
Scenario: Routes with parameters
|
|
142
168
|
Given the `echo` is running with the following manifest:
|
|
143
169
|
"""yaml
|
|
@@ -159,3 +185,40 @@ Feature: Routes
|
|
|
159
185
|
a: foo
|
|
160
186
|
b: bar
|
|
161
187
|
"""
|
|
188
|
+
|
|
189
|
+
Scenario: Route forwarding
|
|
190
|
+
Given the `echo` is running with the following manifest:
|
|
191
|
+
"""yaml
|
|
192
|
+
exposition:
|
|
193
|
+
/show/:a/:b:
|
|
194
|
+
io:output: true
|
|
195
|
+
GET: parameters
|
|
196
|
+
/hello: /echo/show/foo/bar
|
|
197
|
+
/mirror/:a/:b: /echo/show/:a/:b
|
|
198
|
+
"""
|
|
199
|
+
When the following request is received:
|
|
200
|
+
"""
|
|
201
|
+
GET /echo/hello/ HTTP/1.1
|
|
202
|
+
host: nex.toa.io
|
|
203
|
+
accept: application/yaml
|
|
204
|
+
"""
|
|
205
|
+
Then the following reply is sent:
|
|
206
|
+
"""
|
|
207
|
+
200 OK
|
|
208
|
+
|
|
209
|
+
a: foo
|
|
210
|
+
b: bar
|
|
211
|
+
"""
|
|
212
|
+
When the following request is received:
|
|
213
|
+
"""
|
|
214
|
+
GET /echo/mirror/bar/baz/ HTTP/1.1
|
|
215
|
+
host: nex.toa.io
|
|
216
|
+
accept: application/yaml
|
|
217
|
+
"""
|
|
218
|
+
Then the following reply is sent:
|
|
219
|
+
"""
|
|
220
|
+
200 OK
|
|
221
|
+
|
|
222
|
+
a: bar
|
|
223
|
+
b: baz
|
|
224
|
+
"""
|
package/features/steps/IdP.ts
CHANGED
|
@@ -109,7 +109,7 @@ export class IdP {
|
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
iss: IdP.issuer,
|
|
112
|
-
sub:
|
|
112
|
+
sub: user,
|
|
113
113
|
aud: 'test',
|
|
114
114
|
iat: Math.floor(Date.now() / 1000),
|
|
115
115
|
exp: Math.floor((Date.now() + 1000 * 60 * 5) / 1000)
|
|
@@ -134,7 +134,7 @@ export class IdP {
|
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
iss: IdP.issuer,
|
|
137
|
-
sub:
|
|
137
|
+
sub: user,
|
|
138
138
|
aud: 'test',
|
|
139
139
|
iat: Math.floor(Date.now() / 1000),
|
|
140
140
|
exp: Math.floor((Date.now() + 1000 * 60 * 5) / 1000)
|
package/features/vary.feature
CHANGED
|
@@ -214,7 +214,6 @@ Feature: The Vary directive family
|
|
|
214
214
|
"""yaml
|
|
215
215
|
authorities:
|
|
216
216
|
one: the.one.com
|
|
217
|
-
two: the.two.com
|
|
218
217
|
"""
|
|
219
218
|
Given the `echo` is running with the following manifest:
|
|
220
219
|
"""yaml
|
|
@@ -248,5 +247,5 @@ Feature: The Vary directive family
|
|
|
248
247
|
"""
|
|
249
248
|
200 OK
|
|
250
249
|
|
|
251
|
-
Hello two
|
|
250
|
+
Hello the.two.com
|
|
252
251
|
"""
|
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.50",
|
|
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.
|
|
21
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
22
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
20
|
+
"@toa.io/core": "1.0.0-alpha.50",
|
|
21
|
+
"@toa.io/generic": "1.0.0-alpha.50",
|
|
22
|
+
"@toa.io/schemas": "1.0.0-alpha.50",
|
|
23
23
|
"bcryptjs": "2.4.3",
|
|
24
24
|
"error-value": "0.3.0",
|
|
25
25
|
"js-yaml": "4.1.0",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"features:security": "cucumber-js --tags @security"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@toa.io/agent": "1.0.0-alpha.
|
|
48
|
-
"@toa.io/extensions.storages": "1.0.0-alpha.
|
|
47
|
+
"@toa.io/agent": "1.0.0-alpha.50",
|
|
48
|
+
"@toa.io/extensions.storages": "1.0.0-alpha.50",
|
|
49
49
|
"@types/bcryptjs": "2.4.3",
|
|
50
50
|
"@types/cors": "2.8.13",
|
|
51
51
|
"@types/negotiator": "0.6.1"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "514269d4b481150c8cd0db2e5971da6e9fe80ad9"
|
|
54
54
|
}
|
package/schemas/node.cos.yaml
CHANGED
package/source/Gateway.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
1
2
|
import { type bindings, Connector } from '@toa.io/core'
|
|
2
3
|
import * as http from './HTTP'
|
|
3
4
|
import { rethrow } from './exceptions'
|
|
4
5
|
import type { Interception } from './Interception'
|
|
5
|
-
import type { Node, Method, Parameter, Tree } from './RTD'
|
|
6
|
+
import type { Node, Method, Parameter, Tree, Match } from './RTD'
|
|
6
7
|
import type { Label } from './discovery'
|
|
7
8
|
import type { Branch } from './Branch'
|
|
8
9
|
|
|
@@ -28,12 +29,7 @@ export class Gateway extends Connector {
|
|
|
28
29
|
if (interception !== null)
|
|
29
30
|
return interception
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
if (match === null)
|
|
34
|
-
throw new http.NotFound('Route not found')
|
|
35
|
-
|
|
36
|
-
const { node, parameters } = match
|
|
32
|
+
const { node, parameters } = this.match(context)
|
|
37
33
|
|
|
38
34
|
if (context.request.method === 'OPTIONS')
|
|
39
35
|
return await this.explain(node)
|
|
@@ -65,6 +61,31 @@ export class Gateway extends Connector {
|
|
|
65
61
|
console.info('Gateway is closed')
|
|
66
62
|
}
|
|
67
63
|
|
|
64
|
+
private match (context: http.Context): Match {
|
|
65
|
+
const match = this.tree.match(context.url.pathname)
|
|
66
|
+
|
|
67
|
+
if (match === null)
|
|
68
|
+
throw new http.NotFound('Route not found')
|
|
69
|
+
|
|
70
|
+
if (match.node.forward === null)
|
|
71
|
+
return match
|
|
72
|
+
|
|
73
|
+
const destination = match.node.forward.replace(/\/:([^/]+)/g,
|
|
74
|
+
(_, name) => {
|
|
75
|
+
const value = match.parameters.find((parameter) => parameter.name === name)?.value
|
|
76
|
+
|
|
77
|
+
assert.ok(value !== undefined, `Forwarded parameter '${name}' not found`)
|
|
78
|
+
|
|
79
|
+
return `/${value}`
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const forward = this.tree.match(destination)
|
|
83
|
+
|
|
84
|
+
assert.ok(forward !== null, 'Forwarded route not found')
|
|
85
|
+
|
|
86
|
+
return forward
|
|
87
|
+
}
|
|
88
|
+
|
|
68
89
|
private async call (method: Method, context: http.Context, parameters: Parameter[]): Promise<http.OutgoingMessage> {
|
|
69
90
|
if (context.url.pathname[context.url.pathname.length - 1] !== '/')
|
|
70
91
|
throw new http.NotFound('Trailing slash is required')
|
package/source/HTTP/Server.ts
CHANGED
|
@@ -81,13 +81,8 @@ export class Server extends Connector {
|
|
|
81
81
|
return
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const authority = this.authorities[request.headers.host]
|
|
84
|
+
const host = request.headers.host!
|
|
85
|
+
const authority = this.authorities[host] ?? host
|
|
91
86
|
const context = new Context(authority, request as IncomingMessage, this.properties)
|
|
92
87
|
|
|
93
88
|
this.process!(context)
|
package/source/RTD/Node.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type Match, type Parameter } from './Match'
|
|
|
4
4
|
|
|
5
5
|
export class Node {
|
|
6
6
|
public intermediate: boolean
|
|
7
|
+
public forward: string | null
|
|
7
8
|
public methods: Methods
|
|
8
9
|
private readonly protected: boolean
|
|
9
10
|
private routes: Route[]
|
|
@@ -13,6 +14,7 @@ export class Node {
|
|
|
13
14
|
this.routes = routes
|
|
14
15
|
this.methods = methods
|
|
15
16
|
this.protected = properties.protected
|
|
17
|
+
this.forward = properties.forward ?? null
|
|
16
18
|
this.intermediate = this.routes.findIndex((route) => route.root) !== -1
|
|
17
19
|
|
|
18
20
|
this.sort()
|
|
@@ -76,10 +78,15 @@ export class Node {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
private sort (): void {
|
|
79
|
-
this.routes.sort((a, b) =>
|
|
81
|
+
this.routes.sort((a, b) => {
|
|
82
|
+
return a.variables === b.variables
|
|
83
|
+
? b.segments.length - a.segments.length // routes with more segments should be matched first
|
|
84
|
+
: a.variables - b.variables // routes with more variables should be matched last
|
|
85
|
+
})
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
export interface Properties {
|
|
84
90
|
protected: boolean
|
|
91
|
+
forward?: string
|
|
85
92
|
}
|