@toa.io/extensions.exposition 0.20.0-dev.9 → 0.21.0-alpha.0
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/context.toa.yaml +15 -0
- package/components/identity.bans/manifest.toa.yaml +18 -0
- package/components/identity.basic/events/principal.js +9 -0
- package/components/identity.basic/manifest.toa.yaml +50 -0
- package/components/identity.basic/source/authenticate.ts +29 -0
- package/components/identity.basic/source/create.ts +19 -0
- package/components/identity.basic/source/transit.ts +64 -0
- package/components/identity.basic/source/types.ts +42 -0
- package/components/identity.basic/tsconfig.json +9 -0
- package/components/identity.roles/manifest.toa.yaml +31 -0
- package/components/identity.roles/source/list.ts +7 -0
- package/components/identity.roles/source/principal.ts +20 -0
- package/components/identity.roles/tsconfig.json +9 -0
- package/components/identity.tokens/manifest.toa.yaml +39 -0
- package/components/identity.tokens/receivers/identity.bans.updated.js +3 -0
- package/components/identity.tokens/source/authenticate.test.ts +56 -0
- package/components/identity.tokens/source/authenticate.ts +38 -0
- package/components/identity.tokens/source/decrypt.test.ts +59 -0
- package/components/identity.tokens/source/decrypt.ts +25 -0
- package/components/identity.tokens/source/encrypt.test.ts +35 -0
- package/components/identity.tokens/source/encrypt.ts +25 -0
- package/components/identity.tokens/source/revoke.ts +5 -0
- package/components/identity.tokens/source/types.ts +48 -0
- package/components/identity.tokens/tsconfig.json +9 -0
- package/cucumber.js +9 -0
- package/documentation/.assets/ia3-dark.jpg +0 -0
- package/documentation/.assets/ia3-light.jpg +0 -0
- package/documentation/.assets/overview-dark.jpg +0 -0
- package/documentation/.assets/overview-light.jpg +0 -0
- package/documentation/.assets/role-scopes-dark.jpg +0 -0
- package/documentation/.assets/role-scopes-light.jpg +0 -0
- package/documentation/.assets/rtd-dark.jpg +0 -0
- package/documentation/.assets/rtd-light.jpg +0 -0
- package/documentation/access.md +256 -0
- package/documentation/components.md +276 -0
- package/documentation/identity.md +156 -0
- package/documentation/notes/sse.md +71 -0
- package/documentation/protocol.md +18 -0
- package/documentation/query.md +226 -0
- package/documentation/tree.md +169 -0
- package/features/access.feature +448 -0
- package/features/annotation.feature +30 -0
- package/features/body.feature +45 -0
- package/features/directives.feature +56 -0
- package/features/dynamic.feature +99 -0
- package/features/errors.feature +193 -0
- package/features/identity.basic.feature +276 -0
- package/features/identity.feature +61 -0
- package/features/identity.roles.feature +51 -0
- package/features/identity.tokens.feature +119 -0
- package/features/queries.feature +214 -0
- package/features/routes.feature +49 -0
- package/features/steps/Common.ts +10 -0
- package/features/steps/Components.ts +43 -0
- package/features/steps/Database.ts +58 -0
- package/features/steps/Gateway.ts +113 -0
- package/features/steps/HTTP.ts +71 -0
- package/features/steps/Parameters.ts +12 -0
- package/features/steps/Workspace.ts +40 -0
- package/features/steps/components/echo/manifest.toa.yaml +9 -0
- package/features/steps/components/echo/operations/affect.js +7 -0
- package/features/steps/components/echo/operations/compute.js +7 -0
- package/features/steps/components/greeter/manifest.toa.yaml +5 -0
- package/features/steps/components/greeter/operations/greet.js +7 -0
- package/features/steps/components/pots/manifest.toa.yaml +20 -0
- package/features/steps/components/sequences/manifest.toa.yaml +10 -0
- package/features/steps/components/sequences/operations/numbers.js +7 -0
- package/features/steps/components/sequences/operations/tokens.js +16 -0
- package/features/steps/components/users/manifest.toa.yaml +11 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/streams.feature +26 -0
- package/package.json +34 -17
- package/readme.md +183 -0
- package/schemas/annotation.cos.yaml +5 -0
- package/schemas/directive.cos.yaml +3 -0
- package/schemas/method.cos.yaml +8 -0
- package/schemas/node.cos.yaml +5 -0
- package/schemas/query.cos.yaml +17 -0
- package/schemas/querystring.cos.yaml +5 -0
- package/schemas/range.cos.yaml +2 -0
- package/schemas/route.cos.yaml +2 -0
- package/source/Annotation.ts +7 -0
- package/source/Branch.ts +8 -0
- package/source/Composition.ts +57 -0
- package/source/Context.ts +6 -0
- package/source/Directive.test.ts +91 -0
- package/source/Directive.ts +120 -0
- package/source/Endpoint.ts +59 -0
- package/source/Factory.ts +51 -0
- package/source/Gateway.ts +93 -0
- package/source/HTTP/Server.fixtures.ts +45 -0
- package/source/HTTP/Server.test.ts +221 -0
- package/source/HTTP/Server.ts +135 -0
- package/source/HTTP/exceptions.ts +77 -0
- package/source/HTTP/formats/index.ts +19 -0
- package/source/HTTP/formats/json.ts +13 -0
- package/source/HTTP/formats/msgpack.ts +10 -0
- package/source/HTTP/formats/text.ts +9 -0
- package/source/HTTP/formats/yaml.ts +14 -0
- package/source/HTTP/index.ts +3 -0
- package/source/HTTP/messages.test.ts +116 -0
- package/source/HTTP/messages.ts +89 -0
- package/source/Mapping.ts +51 -0
- package/source/Query.test.ts +37 -0
- package/source/Query.ts +105 -0
- package/source/RTD/Context.ts +16 -0
- package/source/RTD/Directives.ts +9 -0
- package/source/RTD/Endpoint.ts +11 -0
- package/source/RTD/Match.ts +16 -0
- package/source/RTD/Method.ts +24 -0
- package/source/RTD/Node.ts +85 -0
- package/source/RTD/Route.ts +59 -0
- package/source/RTD/Tree.ts +54 -0
- package/source/RTD/factory.ts +47 -0
- package/source/RTD/index.ts +8 -0
- package/source/RTD/segment.test.ts +32 -0
- package/source/RTD/segment.ts +25 -0
- package/source/RTD/syntax/index.ts +2 -0
- package/source/RTD/syntax/parse.test.ts +188 -0
- package/source/RTD/syntax/parse.ts +153 -0
- package/source/RTD/syntax/types.ts +48 -0
- package/source/Remotes.test.ts +42 -0
- package/source/Remotes.ts +22 -0
- package/source/Tenant.ts +38 -0
- package/source/deployment.ts +49 -0
- package/source/directives/auth/Anonymous.ts +14 -0
- package/source/directives/auth/Echo.ts +12 -0
- package/source/directives/auth/Family.ts +145 -0
- package/source/directives/auth/Id.ts +19 -0
- package/source/directives/auth/Incept.ts +42 -0
- package/source/directives/auth/Role.test.ts +62 -0
- package/source/directives/auth/Role.ts +56 -0
- package/source/directives/auth/Rule.ts +28 -0
- package/source/directives/auth/Scheme.ts +26 -0
- package/source/directives/auth/index.ts +3 -0
- package/source/directives/auth/schemes.ts +8 -0
- package/source/directives/auth/split.ts +15 -0
- package/source/directives/auth/types.ts +37 -0
- package/source/directives/dev/Family.ts +36 -0
- package/source/directives/dev/Stub.ts +14 -0
- package/source/directives/dev/Throw.ts +14 -0
- package/source/directives/dev/index.ts +3 -0
- package/source/directives/dev/types.ts +5 -0
- package/source/directives/index.ts +5 -0
- package/source/discovery.ts +1 -0
- package/source/exceptions.ts +17 -0
- package/source/index.test.ts +9 -0
- package/source/index.ts +6 -0
- package/source/manifest.test.ts +59 -0
- package/source/manifest.ts +48 -0
- package/source/root.ts +38 -0
- package/source/schemas.ts +9 -0
- package/tsconfig.json +12 -0
- package/src/.manifest/index.js +0 -7
- package/src/.manifest/normalize.js +0 -58
- package/src/.manifest/schema.yaml +0 -71
- package/src/.manifest/validate.js +0 -17
- package/src/constants.js +0 -3
- package/src/deployment.js +0 -23
- package/src/exposition.js +0 -68
- package/src/factory.js +0 -76
- package/src/index.js +0 -9
- package/src/manifest.js +0 -12
- package/src/query/criteria.js +0 -55
- package/src/query/enum.js +0 -35
- package/src/query/index.js +0 -17
- package/src/query/query.js +0 -60
- package/src/query/range.js +0 -28
- package/src/query/sort.js +0 -19
- package/src/remote.js +0 -88
- package/src/server.js +0 -83
- package/src/tenant.js +0 -29
- package/src/translate/etag.js +0 -14
- package/src/translate/index.js +0 -7
- package/src/translate/request.js +0 -68
- package/src/translate/response.js +0 -62
- package/src/tree.js +0 -109
- package/test/manifest.normalize.fixtures.js +0 -37
- package/test/manifest.normalize.test.js +0 -37
- package/test/manifest.validate.test.js +0 -40
- package/test/query.range.test.js +0 -18
- package/test/tree.fixtures.js +0 -21
- package/test/tree.test.js +0 -44
- package/types/annotations.d.ts +0 -10
- package/types/declarations.d.ts +0 -31
- package/types/exposition.d.ts +0 -13
- package/types/http.d.ts +0 -13
- package/types/query.d.ts +0 -16
- package/types/remote.d.ts +0 -19
- package/types/server.d.ts +0 -13
- package/types/tree.d.ts +0 -33
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Feature: Errors
|
|
2
|
+
|
|
3
|
+
Scenario Outline: Missing routes
|
|
4
|
+
Given the `greeter` is running with the following manifest:
|
|
5
|
+
"""yaml
|
|
6
|
+
namespace: basic
|
|
7
|
+
"""
|
|
8
|
+
When the following request is received:
|
|
9
|
+
"""
|
|
10
|
+
GET <path> HTTP/1.1
|
|
11
|
+
accept: application/yaml
|
|
12
|
+
"""
|
|
13
|
+
Then the following reply is sent:
|
|
14
|
+
"""
|
|
15
|
+
404 Not Found
|
|
16
|
+
"""
|
|
17
|
+
Examples:
|
|
18
|
+
| path |
|
|
19
|
+
| /basic/greeter/non-existent-segment/ |
|
|
20
|
+
| /basic/non-existent-component/ |
|
|
21
|
+
| /non-existent-namespace/ |
|
|
22
|
+
|
|
23
|
+
Scenario: Missing trailing slash
|
|
24
|
+
Given the `greeter` is running with the following manifest:
|
|
25
|
+
"""yaml
|
|
26
|
+
namespace: basic
|
|
27
|
+
"""
|
|
28
|
+
When the following request is received:
|
|
29
|
+
"""
|
|
30
|
+
GET /basic/greeter HTTP/1.1
|
|
31
|
+
accept: application/json
|
|
32
|
+
"""
|
|
33
|
+
Then the following reply is sent:
|
|
34
|
+
"""
|
|
35
|
+
404 Not Found
|
|
36
|
+
content-type: application/json
|
|
37
|
+
|
|
38
|
+
"Trailing slash is required."
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
Scenario: Missing method
|
|
42
|
+
Given the `greeter` is running
|
|
43
|
+
When the following request is received:
|
|
44
|
+
"""
|
|
45
|
+
PATCH /greeter/ HTTP/1.1
|
|
46
|
+
accept: application/yaml
|
|
47
|
+
"""
|
|
48
|
+
Then the following reply is sent:
|
|
49
|
+
"""http
|
|
50
|
+
405 Method Not Allowed
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
Scenario: Unsupported method
|
|
54
|
+
When the following request is received:
|
|
55
|
+
"""
|
|
56
|
+
COPY /basic/greeter/ HTTP/1.1
|
|
57
|
+
accept: application/yaml
|
|
58
|
+
"""
|
|
59
|
+
Then the following reply is sent:
|
|
60
|
+
"""http
|
|
61
|
+
501 Not Implemented
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
Scenario: Request body does not match input schema
|
|
65
|
+
Given the `pots` is running with the following manifest:
|
|
66
|
+
"""yaml
|
|
67
|
+
exposition:
|
|
68
|
+
/:
|
|
69
|
+
POST: transit
|
|
70
|
+
"""
|
|
71
|
+
When the following request is received:
|
|
72
|
+
"""
|
|
73
|
+
POST /pots/ HTTP/1.1
|
|
74
|
+
accept: application/yaml
|
|
75
|
+
content-type: application/yaml
|
|
76
|
+
|
|
77
|
+
foo: Hello
|
|
78
|
+
bar: 1.5
|
|
79
|
+
"""
|
|
80
|
+
Then the following reply is sent:
|
|
81
|
+
"""
|
|
82
|
+
400 Bad Request
|
|
83
|
+
content-type: application/yaml
|
|
84
|
+
|
|
85
|
+
must have required property 'title'
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
Scenario: Query limit out of range
|
|
89
|
+
Given the `pots` is running with the following manifest:
|
|
90
|
+
"""yaml
|
|
91
|
+
exposition:
|
|
92
|
+
/:
|
|
93
|
+
GET: enumerate
|
|
94
|
+
"""
|
|
95
|
+
When the following request is received:
|
|
96
|
+
"""
|
|
97
|
+
GET /pots/?limit=1001 HTTP/1.1
|
|
98
|
+
accept: text/plain
|
|
99
|
+
"""
|
|
100
|
+
Then the following reply is sent:
|
|
101
|
+
"""
|
|
102
|
+
400 Bad Request
|
|
103
|
+
content-type: text/plain
|
|
104
|
+
|
|
105
|
+
Query limit must be between 1 and 1000 inclusive.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
Scenario: Closed query criteria
|
|
109
|
+
Given the `pots` is running with the following manifest:
|
|
110
|
+
"""yaml
|
|
111
|
+
exposition:
|
|
112
|
+
/hot:
|
|
113
|
+
GET:
|
|
114
|
+
endpoint: enumerate
|
|
115
|
+
query:
|
|
116
|
+
criteria: temerature>60
|
|
117
|
+
"""
|
|
118
|
+
When the following request is received:
|
|
119
|
+
"""
|
|
120
|
+
GET /pots/hot/?criteria=volume>500 HTTP/1.1
|
|
121
|
+
accept: text/plain
|
|
122
|
+
"""
|
|
123
|
+
Then the following reply is sent:
|
|
124
|
+
"""
|
|
125
|
+
400 Bad Request
|
|
126
|
+
content-type: text/plain
|
|
127
|
+
|
|
128
|
+
Query criteria is closed.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
Scenario: Additional query parameters
|
|
132
|
+
Given the `pots` is running with the following manifest:
|
|
133
|
+
"""yaml
|
|
134
|
+
exposition:
|
|
135
|
+
/:
|
|
136
|
+
GET:
|
|
137
|
+
endpoint: enumerate
|
|
138
|
+
"""
|
|
139
|
+
When the following request is received:
|
|
140
|
+
"""
|
|
141
|
+
GET /pots/?foo=bar HTTP/1.1
|
|
142
|
+
accept: text/plain
|
|
143
|
+
"""
|
|
144
|
+
Then the following reply is sent:
|
|
145
|
+
"""
|
|
146
|
+
400 Bad Request
|
|
147
|
+
content-type: text/plain
|
|
148
|
+
|
|
149
|
+
Query must NOT have additional properties
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
Scenario: Malformed authorization header
|
|
153
|
+
Given the annotation:
|
|
154
|
+
"""yaml
|
|
155
|
+
/:
|
|
156
|
+
GET: {}
|
|
157
|
+
"""
|
|
158
|
+
When the following request is received:
|
|
159
|
+
"""
|
|
160
|
+
GET / HTTP/1.1
|
|
161
|
+
authorization: Basic
|
|
162
|
+
accept: text/plain
|
|
163
|
+
"""
|
|
164
|
+
Then the following reply is sent:
|
|
165
|
+
"""
|
|
166
|
+
401 Unauthorized
|
|
167
|
+
|
|
168
|
+
Malformed authorization header.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
Scenario Outline: Exception is thrown (debug: <debug>)
|
|
172
|
+
Given the annotation:
|
|
173
|
+
"""yaml
|
|
174
|
+
debug: <debug>
|
|
175
|
+
/:
|
|
176
|
+
GET:
|
|
177
|
+
anonymous: true
|
|
178
|
+
dev:throw: Broken!
|
|
179
|
+
"""
|
|
180
|
+
When the following request is received:
|
|
181
|
+
"""
|
|
182
|
+
GET / HTTP/1.1
|
|
183
|
+
accept: text/plain
|
|
184
|
+
"""
|
|
185
|
+
Then the following reply is sent:
|
|
186
|
+
"""
|
|
187
|
+
500 Internal Server Error
|
|
188
|
+
<response>
|
|
189
|
+
"""
|
|
190
|
+
Examples:
|
|
191
|
+
| debug | response |
|
|
192
|
+
| false | content-length: 0 |
|
|
193
|
+
| true | Error: Broken! |
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Feature: Basic authentication
|
|
2
|
+
|
|
3
|
+
Background:
|
|
4
|
+
Given the `identity.basic` database is empty
|
|
5
|
+
|
|
6
|
+
Scenario: Creating new Identity with basic credentials
|
|
7
|
+
When the following request is received:
|
|
8
|
+
"""
|
|
9
|
+
POST /identity/basic/ HTTP/1.1
|
|
10
|
+
content-type: application/yaml
|
|
11
|
+
|
|
12
|
+
username: developer
|
|
13
|
+
password: secret#1234
|
|
14
|
+
"""
|
|
15
|
+
Then the following reply is sent:
|
|
16
|
+
"""
|
|
17
|
+
201 Created
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
Scenario: Creating new Identity using inception
|
|
21
|
+
Given the `users` is running with the following manifest:
|
|
22
|
+
"""yaml
|
|
23
|
+
exposition:
|
|
24
|
+
/:
|
|
25
|
+
anonymous: true # checking compatibility with anonymous access
|
|
26
|
+
POST:
|
|
27
|
+
incept: id
|
|
28
|
+
endpoint: transit
|
|
29
|
+
query: ~
|
|
30
|
+
/:id: # credential testing route
|
|
31
|
+
id: id
|
|
32
|
+
GET: observe
|
|
33
|
+
"""
|
|
34
|
+
When the following request is received:
|
|
35
|
+
"""
|
|
36
|
+
POST /users/ HTTP/1.1
|
|
37
|
+
authorization: Basic dXNlcjpwYXNzMTIzNA==
|
|
38
|
+
accept: application/yaml
|
|
39
|
+
content-type: application/yaml
|
|
40
|
+
|
|
41
|
+
name: Bill Smith
|
|
42
|
+
"""
|
|
43
|
+
Then the following reply is sent:
|
|
44
|
+
"""
|
|
45
|
+
201 Created
|
|
46
|
+
authorization: Token ${{ token }}
|
|
47
|
+
|
|
48
|
+
id: ${{ id }}
|
|
49
|
+
"""
|
|
50
|
+
When the following request is received:
|
|
51
|
+
# basic credentials have been created
|
|
52
|
+
"""
|
|
53
|
+
GET /users/${{ id }}/ HTTP/1.1
|
|
54
|
+
authorization: Basic dXNlcjpwYXNzMTIzNA==
|
|
55
|
+
"""
|
|
56
|
+
Then the following reply is sent:
|
|
57
|
+
"""
|
|
58
|
+
200 OK
|
|
59
|
+
"""
|
|
60
|
+
When the following request is received:
|
|
61
|
+
# valid token has been issued
|
|
62
|
+
"""
|
|
63
|
+
GET /users/${{ id }}/ HTTP/1.1
|
|
64
|
+
authorization: Token ${{ token }}
|
|
65
|
+
"""
|
|
66
|
+
Then the following reply is sent:
|
|
67
|
+
"""
|
|
68
|
+
200 OK
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
Scenario: Changing the password
|
|
72
|
+
Given the annotation:
|
|
73
|
+
"""yaml
|
|
74
|
+
/:
|
|
75
|
+
/:id:
|
|
76
|
+
id: id
|
|
77
|
+
GET:
|
|
78
|
+
dev:stub:
|
|
79
|
+
access: granted!
|
|
80
|
+
"""
|
|
81
|
+
And the `identity.basic` database contains:
|
|
82
|
+
| _id | _version | username | password |
|
|
83
|
+
| efe3a65ebbee47ed95a73edd911ea328 | 1 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
84
|
+
When the following request is received:
|
|
85
|
+
"""
|
|
86
|
+
PATCH /identity/basic/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
87
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
88
|
+
accept: application/yaml
|
|
89
|
+
content-type: application/yaml
|
|
90
|
+
|
|
91
|
+
password: new-secret
|
|
92
|
+
"""
|
|
93
|
+
Then the following reply is sent:
|
|
94
|
+
"""
|
|
95
|
+
200 OK
|
|
96
|
+
"""
|
|
97
|
+
When the following request is received:
|
|
98
|
+
# old password
|
|
99
|
+
"""
|
|
100
|
+
GET /efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
101
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
102
|
+
"""
|
|
103
|
+
Then the following reply is sent:
|
|
104
|
+
"""
|
|
105
|
+
401 Unauthorized
|
|
106
|
+
"""
|
|
107
|
+
When the following request is received:
|
|
108
|
+
# new password
|
|
109
|
+
"""
|
|
110
|
+
GET /efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
111
|
+
authorization: Basic ZGV2ZWxvcGVyOm5ldy1zZWNyZXQ=
|
|
112
|
+
"""
|
|
113
|
+
Then the following reply is sent:
|
|
114
|
+
"""
|
|
115
|
+
200 OK
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
Scenario Outline: <problem> not meeting the requirements
|
|
119
|
+
When the following request is received:
|
|
120
|
+
"""
|
|
121
|
+
POST /identity/basic/ HTTP/1.1
|
|
122
|
+
accept: application/yaml
|
|
123
|
+
content-type: application/yaml
|
|
124
|
+
|
|
125
|
+
username: <username>
|
|
126
|
+
password: <password>
|
|
127
|
+
"""
|
|
128
|
+
Then the following reply is sent:
|
|
129
|
+
"""
|
|
130
|
+
409 Conflict
|
|
131
|
+
|
|
132
|
+
code: <code>
|
|
133
|
+
message: <problem> is not meeting the requirements.
|
|
134
|
+
"""
|
|
135
|
+
Examples:
|
|
136
|
+
| username | password | problem | code |
|
|
137
|
+
| with whitespace | secret#1234 | Username | INVALID_USERNAME |
|
|
138
|
+
| root | short | Password | INVALID_PASSWORD |
|
|
139
|
+
|
|
140
|
+
Scenario Outline: Given <property> is not meeting one of requirements
|
|
141
|
+
Given the `identity.basic` configuration:
|
|
142
|
+
"""yaml
|
|
143
|
+
<property>:
|
|
144
|
+
- ^\S{1,16}$
|
|
145
|
+
- ^[^A]{1,16}$ # should not contain 'A'
|
|
146
|
+
"""
|
|
147
|
+
And the `identity.basic` database contains:
|
|
148
|
+
| _id | _version | username | password |
|
|
149
|
+
| efe3a65ebbee47ed95a73edd911ea328 | 1 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
150
|
+
When the following request is received:
|
|
151
|
+
"""
|
|
152
|
+
PATCH /identity/basic/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
153
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
154
|
+
accept: application/yaml
|
|
155
|
+
content-type: application/yaml
|
|
156
|
+
|
|
157
|
+
<property>: hasAinside
|
|
158
|
+
"""
|
|
159
|
+
Then the following reply is sent:
|
|
160
|
+
"""
|
|
161
|
+
409 Conflict
|
|
162
|
+
"""
|
|
163
|
+
Examples:
|
|
164
|
+
| property |
|
|
165
|
+
| username |
|
|
166
|
+
| password |
|
|
167
|
+
|
|
168
|
+
Scenario: Granting a `system` role to a Principal
|
|
169
|
+
Given the `identity.basic` configuration:
|
|
170
|
+
"""yaml
|
|
171
|
+
principal: root
|
|
172
|
+
"""
|
|
173
|
+
And the annotation:
|
|
174
|
+
"""yaml
|
|
175
|
+
/:
|
|
176
|
+
GET:
|
|
177
|
+
auth:role: system:stub
|
|
178
|
+
dev:stub:
|
|
179
|
+
access: granted!
|
|
180
|
+
"""
|
|
181
|
+
When the following request is received:
|
|
182
|
+
"""
|
|
183
|
+
POST /identity/basic/ HTTP/1.1
|
|
184
|
+
content-type: application/yaml
|
|
185
|
+
|
|
186
|
+
username: root
|
|
187
|
+
password: secret#1234
|
|
188
|
+
"""
|
|
189
|
+
Then the following reply is sent:
|
|
190
|
+
"""
|
|
191
|
+
201 Created
|
|
192
|
+
|
|
193
|
+
id: ${{ id }}
|
|
194
|
+
"""
|
|
195
|
+
# role granting is eventual
|
|
196
|
+
Then after 0.1 seconds
|
|
197
|
+
When the following request is received:
|
|
198
|
+
"""
|
|
199
|
+
GET /identity/roles/${{ id }}/ HTTP/1.1
|
|
200
|
+
authorization: Basic cm9vdDpzZWNyZXQjMTIzNA==
|
|
201
|
+
accept: application/yaml
|
|
202
|
+
"""
|
|
203
|
+
Then the following reply is sent:
|
|
204
|
+
"""
|
|
205
|
+
200 OK
|
|
206
|
+
authorization: Token ${{ token }}
|
|
207
|
+
|
|
208
|
+
- system
|
|
209
|
+
"""
|
|
210
|
+
When the following request is received:
|
|
211
|
+
"""
|
|
212
|
+
GET / HTTP/1.1
|
|
213
|
+
authorization: Token ${{ token }}
|
|
214
|
+
accept: application/yaml
|
|
215
|
+
"""
|
|
216
|
+
Then the following reply is sent:
|
|
217
|
+
"""
|
|
218
|
+
200 OK
|
|
219
|
+
|
|
220
|
+
access: granted!
|
|
221
|
+
"""
|
|
222
|
+
# Principal username cannot be changed
|
|
223
|
+
When the following request is received:
|
|
224
|
+
"""
|
|
225
|
+
PATCH /identity/basic/${{ id }}/ HTTP/1.1
|
|
226
|
+
authorization: Token ${{ token }}
|
|
227
|
+
content-type: application/yaml
|
|
228
|
+
|
|
229
|
+
username: admin
|
|
230
|
+
"""
|
|
231
|
+
Then the following reply is sent:
|
|
232
|
+
"""
|
|
233
|
+
409 Conflict
|
|
234
|
+
|
|
235
|
+
code: PRINCIPAL_LOCKED
|
|
236
|
+
message: Principal username cannot be changed.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
Scenario: Creating an Identity using inception with existing credentials
|
|
240
|
+
Given the `identity.basic` database is empty
|
|
241
|
+
And the `users` is running with the following manifest:
|
|
242
|
+
"""yaml
|
|
243
|
+
exposition:
|
|
244
|
+
/:
|
|
245
|
+
anonymous: true
|
|
246
|
+
POST:
|
|
247
|
+
incept: id
|
|
248
|
+
endpoint: transit
|
|
249
|
+
"""
|
|
250
|
+
When the following request is received:
|
|
251
|
+
# identity inception
|
|
252
|
+
"""
|
|
253
|
+
POST /users/ HTTP/1.1
|
|
254
|
+
authorization: Basic dXNlcjpwYXNzMTIzNA==
|
|
255
|
+
accept: application/yaml
|
|
256
|
+
content-type: application/yaml
|
|
257
|
+
|
|
258
|
+
name: Bill Smith
|
|
259
|
+
"""
|
|
260
|
+
Then the following reply is sent:
|
|
261
|
+
"""
|
|
262
|
+
201 Created
|
|
263
|
+
"""
|
|
264
|
+
And the following request is received:
|
|
265
|
+
# same credentials
|
|
266
|
+
"""
|
|
267
|
+
POST /users/ HTTP/1.1
|
|
268
|
+
authorization: Basic dXNlcjpwYXNzMTIzNA==
|
|
269
|
+
content-type: text/plain
|
|
270
|
+
|
|
271
|
+
name: Mary Louis
|
|
272
|
+
"""
|
|
273
|
+
Then the following reply is sent:
|
|
274
|
+
"""
|
|
275
|
+
403 Forbidden
|
|
276
|
+
"""
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Feature: Identity resource
|
|
2
|
+
|
|
3
|
+
Scenario: Requesting own Identity
|
|
4
|
+
Given the `identity.basic` database contains:
|
|
5
|
+
| _id | username | password |
|
|
6
|
+
| efe3a65ebbee47ed95a73edd911ea328 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
7
|
+
And the `identity.roles` database contains:
|
|
8
|
+
| _id | identity | role |
|
|
9
|
+
| 9c4702490ff84f2a9e1b1da2ab64bdd4 | efe3a65ebbee47ed95a73edd911ea328 | developer |
|
|
10
|
+
| 88ec8e348a5c48aaa2d25faa904cd9ff | efe3a65ebbee47ed95a73edd911ea328 | system:identity |
|
|
11
|
+
When the following request is received:
|
|
12
|
+
"""
|
|
13
|
+
GET /identity/ HTTP/1.1
|
|
14
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
15
|
+
accept: application/yaml
|
|
16
|
+
"""
|
|
17
|
+
Then the following reply is sent:
|
|
18
|
+
"""
|
|
19
|
+
200 OK
|
|
20
|
+
authorization: Token ${{ token }}
|
|
21
|
+
|
|
22
|
+
id: efe3a65ebbee47ed95a73edd911ea328
|
|
23
|
+
roles:
|
|
24
|
+
- developer
|
|
25
|
+
- system:identity
|
|
26
|
+
"""
|
|
27
|
+
When the following request is received:
|
|
28
|
+
"""
|
|
29
|
+
GET /identity/ HTTP/1.1
|
|
30
|
+
authorization: Token ${{ token }}
|
|
31
|
+
accept: application/yaml
|
|
32
|
+
"""
|
|
33
|
+
Then the following reply is sent:
|
|
34
|
+
"""
|
|
35
|
+
200 OK
|
|
36
|
+
|
|
37
|
+
id: efe3a65ebbee47ed95a73edd911ea328
|
|
38
|
+
roles:
|
|
39
|
+
- developer
|
|
40
|
+
- system:identity
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
Scenario: Requesting Identity with non-existent credentials
|
|
44
|
+
Given the `identity.basic` database is empty
|
|
45
|
+
When the following request is received:
|
|
46
|
+
"""
|
|
47
|
+
GET /identity/ HTTP/1.1
|
|
48
|
+
authorization: Basic dXNlcjpwYXNzMTIzNA==
|
|
49
|
+
"""
|
|
50
|
+
Then the following reply is sent:
|
|
51
|
+
"""
|
|
52
|
+
401 Unauthorized
|
|
53
|
+
"""
|
|
54
|
+
When the following request is received:
|
|
55
|
+
"""
|
|
56
|
+
GET /identity/ HTTP/1.1
|
|
57
|
+
"""
|
|
58
|
+
Then the following reply is sent:
|
|
59
|
+
"""
|
|
60
|
+
401 Unauthorized
|
|
61
|
+
"""
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Feature: Roles management
|
|
2
|
+
|
|
3
|
+
Scenario: Adding a role to an Identity
|
|
4
|
+
Given the `identity.basic` database contains:
|
|
5
|
+
| _id | username | password |
|
|
6
|
+
| 72cf9b0ab0ac4ab2b8036e4e940ddcae | root | $2b$10$Qq/qnyyU5wjrbDXyWok14OnqAZv/z.pLhz.UddatjI6eHU/rFof4i |
|
|
7
|
+
| 4344518184ad44228baffce7a44fd0b1 | user | $2b$10$JoiAQUS7tzobDAFIDBWhWeEIJv933dQetyjRzSmfQGaJE5ZlJbmYy |
|
|
8
|
+
And the `identity.roles` database contains:
|
|
9
|
+
| _id | identity | role |
|
|
10
|
+
| 9c4702490ff84f2a9e1b1da2ab64bdd4 | 72cf9b0ab0ac4ab2b8036e4e940ddcae | system:identity:roles |
|
|
11
|
+
And the annotation:
|
|
12
|
+
"""yaml
|
|
13
|
+
/:
|
|
14
|
+
auth:role: test
|
|
15
|
+
GET:
|
|
16
|
+
dev:stub:
|
|
17
|
+
access: granted!
|
|
18
|
+
"""
|
|
19
|
+
When the following request is received:
|
|
20
|
+
# user doesn't have the required role
|
|
21
|
+
"""
|
|
22
|
+
GET / HTTP/1.1
|
|
23
|
+
authorization: Basic dXNlcjpwYXNz
|
|
24
|
+
"""
|
|
25
|
+
Then the following reply is sent:
|
|
26
|
+
"""
|
|
27
|
+
403 Forbidden
|
|
28
|
+
"""
|
|
29
|
+
When the following request is received:
|
|
30
|
+
# root adds a role to a user
|
|
31
|
+
"""
|
|
32
|
+
POST /identity/roles/4344518184ad44228baffce7a44fd0b1/ HTTP/1.1
|
|
33
|
+
authorization: Basic cm9vdDpzZWNyZXQ=
|
|
34
|
+
content-type: application/yaml
|
|
35
|
+
|
|
36
|
+
role: test
|
|
37
|
+
"""
|
|
38
|
+
Then the following reply is sent:
|
|
39
|
+
"""
|
|
40
|
+
201 Created
|
|
41
|
+
"""
|
|
42
|
+
When the following request is received:
|
|
43
|
+
# user now have the role
|
|
44
|
+
"""
|
|
45
|
+
GET / HTTP/1.1
|
|
46
|
+
authorization: Basic dXNlcjpwYXNz
|
|
47
|
+
"""
|
|
48
|
+
Then the following reply is sent:
|
|
49
|
+
"""
|
|
50
|
+
200 OK
|
|
51
|
+
"""
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Feature: Tokens lifecycle
|
|
2
|
+
|
|
3
|
+
Scenario: Switching to Token authentication scheme
|
|
4
|
+
Given the `identity.basic` database contains:
|
|
5
|
+
| _id | username | password |
|
|
6
|
+
| efe3a65ebbee47ed95a73edd911ea328 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
7
|
+
Given the annotation:
|
|
8
|
+
"""yaml
|
|
9
|
+
/:
|
|
10
|
+
/hello/:id:
|
|
11
|
+
auth:id: id
|
|
12
|
+
GET:
|
|
13
|
+
dev:stub: Hello
|
|
14
|
+
"""
|
|
15
|
+
When the following request is received:
|
|
16
|
+
"""
|
|
17
|
+
GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
18
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
19
|
+
accept: text/plain
|
|
20
|
+
"""
|
|
21
|
+
Then the following reply is sent:
|
|
22
|
+
"""
|
|
23
|
+
200 OK
|
|
24
|
+
content-type: text/plain
|
|
25
|
+
authorization: Token ${{ token }}
|
|
26
|
+
|
|
27
|
+
Hello
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
Scenario: Refreshing stale token
|
|
31
|
+
Given the `identity.tokens` configuration:
|
|
32
|
+
"""yaml
|
|
33
|
+
refresh: 1
|
|
34
|
+
"""
|
|
35
|
+
And the annotation:
|
|
36
|
+
"""yaml
|
|
37
|
+
/:
|
|
38
|
+
/hello/:id:
|
|
39
|
+
auth:id: id
|
|
40
|
+
GET:
|
|
41
|
+
dev:stub: Hello
|
|
42
|
+
"""
|
|
43
|
+
When the following request is received:
|
|
44
|
+
"""
|
|
45
|
+
GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
46
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
47
|
+
accept: text/plain
|
|
48
|
+
"""
|
|
49
|
+
Then the following reply is sent:
|
|
50
|
+
"""
|
|
51
|
+
200 OK
|
|
52
|
+
authorization: Token ${{ token }}
|
|
53
|
+
|
|
54
|
+
Hello
|
|
55
|
+
"""
|
|
56
|
+
Then after 1 second
|
|
57
|
+
When the following request is received:
|
|
58
|
+
"""
|
|
59
|
+
GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
60
|
+
authorization: Token ${{ token }}
|
|
61
|
+
accept: text/plain
|
|
62
|
+
"""
|
|
63
|
+
Then the following reply is sent:
|
|
64
|
+
"""
|
|
65
|
+
200 OK
|
|
66
|
+
authorization: Token
|
|
67
|
+
|
|
68
|
+
Hello
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
Scenario: Token revocation on password change
|
|
72
|
+
Given the annotation:
|
|
73
|
+
"""yaml
|
|
74
|
+
/:
|
|
75
|
+
/:id:
|
|
76
|
+
id: id
|
|
77
|
+
GET:
|
|
78
|
+
dev:stub:
|
|
79
|
+
access: granted!
|
|
80
|
+
"""
|
|
81
|
+
And the `identity.tokens` configuration:
|
|
82
|
+
"""yaml
|
|
83
|
+
refresh: 0.1
|
|
84
|
+
"""
|
|
85
|
+
And the `identity.basic` database contains:
|
|
86
|
+
| _id | _version | username | password |
|
|
87
|
+
| efe3a65ebbee47ed95a73edd911ea328 | 1 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
88
|
+
When the following request is received:
|
|
89
|
+
"""
|
|
90
|
+
GET /efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
91
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
92
|
+
"""
|
|
93
|
+
Then the following reply is sent:
|
|
94
|
+
"""
|
|
95
|
+
200 OK
|
|
96
|
+
authorization: Token ${{ token }}
|
|
97
|
+
"""
|
|
98
|
+
When the following request is received:
|
|
99
|
+
"""
|
|
100
|
+
PATCH /identity/basic/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
101
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
102
|
+
content-type: application/yaml
|
|
103
|
+
|
|
104
|
+
password: new-secret
|
|
105
|
+
"""
|
|
106
|
+
Then the following reply is sent:
|
|
107
|
+
"""
|
|
108
|
+
200 OK
|
|
109
|
+
"""
|
|
110
|
+
Then after 0.2 seconds
|
|
111
|
+
When the following request is received:
|
|
112
|
+
"""
|
|
113
|
+
GET /efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
|
|
114
|
+
authorization: Token ${{ token }}
|
|
115
|
+
"""
|
|
116
|
+
Then the following reply is sent:
|
|
117
|
+
"""
|
|
118
|
+
401 Unauthorized
|
|
119
|
+
"""
|