@reventlessdev/reventless-aws 3.0.0-alpha.394 → 3.0.0-alpha.396
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/CHANGELOG.md +22 -0
- package/package.json +8 -8
- package/scripts/DeployApp.res +7 -1
- package/scripts/DeployApp.res.mjs +1 -1
- package/scripts/ProvisionAccounts.res +89 -4
- package/scripts/ProvisionAccounts.res.mjs +101 -5
- package/scripts/ProvisionIdentity.res +42 -18
- package/scripts/ProvisionIdentity.res.mjs +40 -26
- package/src/Platform.res +47 -10
- package/src/Platform.res.mjs +44 -10
- package/src/Platform_Stack.res +17 -0
- package/src/Platform_Stack.res.mjs +7 -1
- package/src/adapter/Auth/Auth_ActiveRoleTrigger.res +16 -7
- package/src/adapter/Auth/Auth_ActiveRoleTrigger.res.mjs +16 -3
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +59 -9
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res.mjs +53 -6
- package/src/adapter/Auth/Auth_Cognito.res +13 -2
- package/src/adapter/Auth/Auth_Cognito.res.mjs +9 -2
- package/src/adapter/Auth/Auth_IdentityStore.res +52 -0
- package/src/adapter/Auth/Auth_IdentityStore.res.mjs +46 -0
- package/src/adapter/Auth/Auth_IdentityStore_Schema.res +39 -0
- package/src/adapter/Auth/Auth_IdentityStore_Schema.res.mjs +35 -0
- package/src/adapter/Identity/IdentityProvider_Cognito_Backend.res +251 -0
- package/src/adapter/Identity/IdentityProvider_Cognito_Backend.res.mjs +300 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +7 -4
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +2 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +53 -2
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +2 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res +23 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +29 -0
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +40 -0
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res.mjs +59 -8
- package/tests/Auth_CognitoTest.res +62 -0
- package/tests/Auth_CognitoTest.res.mjs +98 -0
- package/tests/Auth_IdentityStore_SchemaTest.res +35 -0
- package/tests/Auth_IdentityStore_SchemaTest.res.mjs +43 -0
- package/tests/IdentityProvider_Cognito_BackendTest.res +314 -0
- package/tests/IdentityProvider_Cognito_BackendTest.res.mjs +442 -0
- package/tests/ProvisionAccountsTest.res.mjs +1 -1
- package/tests/Util_ShellConfigTest.res.mjs +1 -22
|
@@ -171,3 +171,43 @@ describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
171
171
|
)->toEqual(Some(["arn:aws:iam::1:role/Shopper"]))
|
|
172
172
|
})
|
|
173
173
|
})
|
|
174
|
+
|
|
175
|
+
// ── The domain id claim ─────────────────────────────────────────────────────
|
|
176
|
+
//
|
|
177
|
+
// Rides on whatever else the mint adds. The regression line above still holds
|
|
178
|
+
// for every account created before the identity store existed, because such an
|
|
179
|
+
// account has no row and so no `userId` arrives here.
|
|
180
|
+
|
|
181
|
+
describe("Auth_ActiveRoleTrigger_Ops.respond with a domain id", () => {
|
|
182
|
+
testSync("no stored role and a domain id adds the claim and nothing else", () => {
|
|
183
|
+
let input = eventFor(~membership=["Admin", "Shopper"])
|
|
184
|
+
let event = Trigger.respond(~event=input, ~decision=Unchanged, ~userId="u_abc")
|
|
185
|
+
expect(claimOf(event, ReventlessCore.Auth_UserId.claim))->toEqual(Some("u_abc"))
|
|
186
|
+
expect(groupsOf(event))->toEqual(None)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
testSync("narrowing and a domain id carry both", () => {
|
|
190
|
+
let event = Trigger.respond(
|
|
191
|
+
~event=eventFor(~membership=["Admin", "Shopper"]),
|
|
192
|
+
~decision=Narrow({role: "Shopper", membership: ["Admin", "Shopper"]}),
|
|
193
|
+
~userId="u_abc",
|
|
194
|
+
)
|
|
195
|
+
expect(groupsOf(event))->toEqual(Some(["Shopper"]))
|
|
196
|
+
expect((
|
|
197
|
+
claimOf(event, Contract.activeRoleClaim),
|
|
198
|
+
claimOf(event, ReventlessCore.Auth_UserId.claim),
|
|
199
|
+
))->toEqual((Some("Shopper"), Some("u_abc")))
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
testSync("no domain id leaves a narrowed token without the claim", () => {
|
|
203
|
+
let event = Trigger.respond(
|
|
204
|
+
~event=eventFor(~membership=["Admin", "Shopper"]),
|
|
205
|
+
~decision=Narrow({role: "Shopper", membership: ["Admin", "Shopper"]}),
|
|
206
|
+
)
|
|
207
|
+
expect(claimOf(event, ReventlessCore.Auth_UserId.claim))->toEqual(None)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
testPromise("no identity store configured reads as no row", async () =>
|
|
211
|
+
expect(await Trigger.userIdFor(~sub="u-carol", ~table=None))->toEqual(None)
|
|
212
|
+
)
|
|
213
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
|
|
4
5
|
import * as Auth_ActiveRole$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_ActiveRole.res.mjs";
|
|
5
6
|
import * as Auth_ActiveRoleTrigger_Ops$ReventlessAws from "../src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res.mjs";
|
|
6
7
|
|
|
@@ -43,7 +44,7 @@ globalThis.describe("the conformance table, minted by the trigger", () => {
|
|
|
43
44
|
let requested = param.requested;
|
|
44
45
|
let membership = param.membership;
|
|
45
46
|
globalThis.test(param.label, () => {
|
|
46
|
-
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(eventFor(membership, undefined, undefined), Auth_ActiveRoleTrigger_Ops$ReventlessAws.decide(membership, requested));
|
|
47
|
+
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(eventFor(membership, undefined, undefined), Auth_ActiveRoleTrigger_Ops$ReventlessAws.decide(membership, requested), undefined);
|
|
47
48
|
globalThis.expect(Stdlib_Option.getOr(groupsOf(event), membership)).toEqual(Stdlib_Option.getOr(expected, membership));
|
|
48
49
|
});
|
|
49
50
|
});
|
|
@@ -84,7 +85,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
84
85
|
"Admin",
|
|
85
86
|
"Shopper"
|
|
86
87
|
], undefined, undefined);
|
|
87
|
-
globalThis.expect(Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(input, "Unchanged")).toEqual(input);
|
|
88
|
+
globalThis.expect(Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(input, "Unchanged", undefined)).toEqual(input);
|
|
88
89
|
});
|
|
89
90
|
globalThis.test("narrowing puts the chosen role alone in the group claim", () => {
|
|
90
91
|
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(eventFor([
|
|
@@ -97,7 +98,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
97
98
|
"Admin",
|
|
98
99
|
"Shopper"
|
|
99
100
|
]
|
|
100
|
-
});
|
|
101
|
+
}, undefined);
|
|
101
102
|
globalThis.expect(groupsOf(event)).toEqual(["Shopper"]);
|
|
102
103
|
});
|
|
103
104
|
globalThis.test("a narrowed token records the choice and what it gave up", () => {
|
|
@@ -111,7 +112,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
111
112
|
"Admin",
|
|
112
113
|
"Shopper"
|
|
113
114
|
]
|
|
114
|
-
});
|
|
115
|
+
}, undefined);
|
|
115
116
|
globalThis.expect([
|
|
116
117
|
claimOf(event, Auth_ActiveRole$ReventlessCore.activeRoleClaim),
|
|
117
118
|
claimOf(event, Auth_ActiveRole$ReventlessCore.availableRolesClaim)
|
|
@@ -125,7 +126,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
125
126
|
TAG: "Stale",
|
|
126
127
|
role: "Admin",
|
|
127
128
|
membership: ["Shopper"]
|
|
128
|
-
});
|
|
129
|
+
}, undefined);
|
|
129
130
|
globalThis.expect([
|
|
130
131
|
groupsOf(event),
|
|
131
132
|
claimOf(event, Auth_ActiveRole$ReventlessCore.staleRoleClaim)
|
|
@@ -139,7 +140,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
139
140
|
TAG: "Stale",
|
|
140
141
|
role: "Admin",
|
|
141
142
|
membership: ["Shopper"]
|
|
142
|
-
});
|
|
143
|
+
}, undefined);
|
|
143
144
|
globalThis.expect(claimOf(event, Auth_ActiveRole$ReventlessCore.activeRoleClaim)).toEqual(undefined);
|
|
144
145
|
});
|
|
145
146
|
globalThis.test("narrowing carries the caller's IAM roles through untouched", () => {
|
|
@@ -153,7 +154,7 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
153
154
|
"Admin",
|
|
154
155
|
"Shopper"
|
|
155
156
|
]
|
|
156
|
-
});
|
|
157
|
+
}, undefined);
|
|
157
158
|
let details = Stdlib_Option.flatMap(overrideOf(event), c => c.groupOverrideDetails);
|
|
158
159
|
globalThis.expect([
|
|
159
160
|
Stdlib_Option.map(details, d => d.iamRolesToOverride),
|
|
@@ -168,11 +169,61 @@ globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
168
169
|
TAG: "Stale",
|
|
169
170
|
role: "Admin",
|
|
170
171
|
membership: ["Shopper"]
|
|
171
|
-
});
|
|
172
|
+
}, undefined);
|
|
172
173
|
globalThis.expect(Stdlib_Option.map(Stdlib_Option.flatMap(overrideOf(event), c => c.groupOverrideDetails), d => d.iamRolesToOverride)).toEqual(["arn:aws:iam::1:role/Shopper"]);
|
|
173
174
|
});
|
|
174
175
|
});
|
|
175
176
|
|
|
177
|
+
globalThis.describe("Auth_ActiveRoleTrigger_Ops.respond with a domain id", () => {
|
|
178
|
+
globalThis.test("no stored role and a domain id adds the claim and nothing else", () => {
|
|
179
|
+
let input = eventFor([
|
|
180
|
+
"Admin",
|
|
181
|
+
"Shopper"
|
|
182
|
+
], undefined, undefined);
|
|
183
|
+
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(input, "Unchanged", "u_abc");
|
|
184
|
+
globalThis.expect(claimOf(event, Auth_UserId$ReventlessCore.claim)).toEqual("u_abc");
|
|
185
|
+
globalThis.expect(groupsOf(event)).toEqual(undefined);
|
|
186
|
+
});
|
|
187
|
+
globalThis.test("narrowing and a domain id carry both", () => {
|
|
188
|
+
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(eventFor([
|
|
189
|
+
"Admin",
|
|
190
|
+
"Shopper"
|
|
191
|
+
], undefined, undefined), {
|
|
192
|
+
TAG: "Narrow",
|
|
193
|
+
role: "Shopper",
|
|
194
|
+
membership: [
|
|
195
|
+
"Admin",
|
|
196
|
+
"Shopper"
|
|
197
|
+
]
|
|
198
|
+
}, "u_abc");
|
|
199
|
+
globalThis.expect(groupsOf(event)).toEqual(["Shopper"]);
|
|
200
|
+
globalThis.expect([
|
|
201
|
+
claimOf(event, Auth_ActiveRole$ReventlessCore.activeRoleClaim),
|
|
202
|
+
claimOf(event, Auth_UserId$ReventlessCore.claim)
|
|
203
|
+
]).toEqual([
|
|
204
|
+
"Shopper",
|
|
205
|
+
"u_abc"
|
|
206
|
+
]);
|
|
207
|
+
});
|
|
208
|
+
globalThis.test("no domain id leaves a narrowed token without the claim", () => {
|
|
209
|
+
let event = Auth_ActiveRoleTrigger_Ops$ReventlessAws.respond(eventFor([
|
|
210
|
+
"Admin",
|
|
211
|
+
"Shopper"
|
|
212
|
+
], undefined, undefined), {
|
|
213
|
+
TAG: "Narrow",
|
|
214
|
+
role: "Shopper",
|
|
215
|
+
membership: [
|
|
216
|
+
"Admin",
|
|
217
|
+
"Shopper"
|
|
218
|
+
]
|
|
219
|
+
}, undefined);
|
|
220
|
+
globalThis.expect(claimOf(event, Auth_UserId$ReventlessCore.claim)).toEqual(undefined);
|
|
221
|
+
});
|
|
222
|
+
globalThis.test("no identity store configured reads as no row", async () => {
|
|
223
|
+
globalThis.expect(await Auth_ActiveRoleTrigger_Ops$ReventlessAws.userIdFor("u-carol", undefined)).toEqual(undefined);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
176
227
|
let Trigger;
|
|
177
228
|
|
|
178
229
|
let Contract;
|
|
@@ -186,3 +186,65 @@ describe("Auth_Cognito.fromAppSyncIdentity", () => {
|
|
|
186
186
|
}
|
|
187
187
|
})
|
|
188
188
|
})
|
|
189
|
+
|
|
190
|
+
// ── The domain id claim ─────────────────────────────────────────────────────
|
|
191
|
+
//
|
|
192
|
+
// Both arms, on both entry points. The second arm is the one that will look
|
|
193
|
+
// redundant to a later reader and is not: every `@owner` field written before
|
|
194
|
+
// the identity store existed holds a `sub`, and it keeps matching only while a
|
|
195
|
+
// token without the claim produces exactly that `sub`.
|
|
196
|
+
|
|
197
|
+
describe("Auth_Cognito and the domain id claim", () => {
|
|
198
|
+
let claimName = ReventlessCore.Auth_UserId.claim
|
|
199
|
+
|
|
200
|
+
testAsync("authenticate: a token carrying the claim yields it, not the sub", async () => {
|
|
201
|
+
let token = buildJwt(
|
|
202
|
+
Dict.fromArray([
|
|
203
|
+
("sub", JSON.Encode.string("c3741234-aaaa")),
|
|
204
|
+
(claimName, JSON.Encode.string("u_0123")),
|
|
205
|
+
]),
|
|
206
|
+
)
|
|
207
|
+
switch await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)])) {
|
|
208
|
+
| Authenticated(identity) => expect(identity.userId)->toBe("u_0123")
|
|
209
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
testAsync("authenticate: a token without the claim yields exactly the sub", async () => {
|
|
214
|
+
let token = buildJwt(Dict.fromArray([("sub", JSON.Encode.string("c3741234-aaaa"))]))
|
|
215
|
+
switch await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)])) {
|
|
216
|
+
| Authenticated(identity) => expect(identity.userId)->toBe("c3741234-aaaa")
|
|
217
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
testSync("fromAppSyncIdentity: the claim wins over the sub", () => {
|
|
222
|
+
let id: Auth_Cognito.appSyncIdentity = {
|
|
223
|
+
sub: "c3741234-aaaa",
|
|
224
|
+
claims: Dict.fromArray([(claimName, JSON.Encode.string("u_0123"))]),
|
|
225
|
+
}
|
|
226
|
+
switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
|
|
227
|
+
| Authenticated(identity) => expect(identity.userId)->toBe("u_0123")
|
|
228
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
testSync("fromAppSyncIdentity: no claim is exactly the sub", () => {
|
|
233
|
+
let id: Auth_Cognito.appSyncIdentity = {sub: "c3741234-aaaa", claims: Dict.make()}
|
|
234
|
+
switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
|
|
235
|
+
| Authenticated(identity) => expect(identity.userId)->toBe("c3741234-aaaa")
|
|
236
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
testSync("an empty claim is treated as absent", () => {
|
|
241
|
+
let id: Auth_Cognito.appSyncIdentity = {
|
|
242
|
+
sub: "c3741234-aaaa",
|
|
243
|
+
claims: Dict.fromArray([(claimName, JSON.Encode.string(""))]),
|
|
244
|
+
}
|
|
245
|
+
switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
|
|
246
|
+
| Authenticated(identity) => expect(identity.userId)->toBe("c3741234-aaaa")
|
|
247
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
})
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
4
|
import * as Auth_Cognito$ReventlessAws from "../src/adapter/Auth/Auth_Cognito.res.mjs";
|
|
5
|
+
import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
|
|
5
6
|
|
|
6
7
|
function buildContext(headers) {
|
|
7
8
|
return {
|
|
@@ -240,6 +241,103 @@ globalThis.describe("Auth_Cognito.fromAppSyncIdentity", () => {
|
|
|
240
241
|
});
|
|
241
242
|
});
|
|
242
243
|
|
|
244
|
+
globalThis.describe("Auth_Cognito and the domain id claim", () => {
|
|
245
|
+
globalThis.test("authenticate: a token carrying the claim yields it, not the sub", async () => {
|
|
246
|
+
let token = buildJwt(Object.fromEntries([
|
|
247
|
+
[
|
|
248
|
+
"sub",
|
|
249
|
+
"c3741234-aaaa"
|
|
250
|
+
],
|
|
251
|
+
[
|
|
252
|
+
Auth_UserId$ReventlessCore.claim,
|
|
253
|
+
"u_0123"
|
|
254
|
+
]
|
|
255
|
+
]));
|
|
256
|
+
let identity = await Auth_Cognito$ReventlessAws.authenticate(buildContext([[
|
|
257
|
+
"authorization",
|
|
258
|
+
"Bearer " + token
|
|
259
|
+
]]));
|
|
260
|
+
if (typeof identity !== "object") {
|
|
261
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
262
|
+
}
|
|
263
|
+
if (identity.TAG !== "Authenticated") {
|
|
264
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
265
|
+
}
|
|
266
|
+
globalThis.expect(identity._0.userId).toBe("u_0123");
|
|
267
|
+
});
|
|
268
|
+
globalThis.test("authenticate: a token without the claim yields exactly the sub", async () => {
|
|
269
|
+
let token = buildJwt(Object.fromEntries([[
|
|
270
|
+
"sub",
|
|
271
|
+
"c3741234-aaaa"
|
|
272
|
+
]]));
|
|
273
|
+
let identity = await Auth_Cognito$ReventlessAws.authenticate(buildContext([[
|
|
274
|
+
"authorization",
|
|
275
|
+
"Bearer " + token
|
|
276
|
+
]]));
|
|
277
|
+
if (typeof identity !== "object") {
|
|
278
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
279
|
+
}
|
|
280
|
+
if (identity.TAG !== "Authenticated") {
|
|
281
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
282
|
+
}
|
|
283
|
+
globalThis.expect(identity._0.userId).toBe("c3741234-aaaa");
|
|
284
|
+
});
|
|
285
|
+
globalThis.test("fromAppSyncIdentity: the claim wins over the sub", () => {
|
|
286
|
+
let id_sub = "c3741234-aaaa";
|
|
287
|
+
let id_claims = Object.fromEntries([[
|
|
288
|
+
Auth_UserId$ReventlessCore.claim,
|
|
289
|
+
"u_0123"
|
|
290
|
+
]]);
|
|
291
|
+
let id = {
|
|
292
|
+
sub: id_sub,
|
|
293
|
+
claims: id_claims
|
|
294
|
+
};
|
|
295
|
+
let identity = Auth_Cognito$ReventlessAws.fromAppSyncIdentity(id);
|
|
296
|
+
if (typeof identity !== "object") {
|
|
297
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
298
|
+
}
|
|
299
|
+
if (identity.TAG !== "Authenticated") {
|
|
300
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
301
|
+
}
|
|
302
|
+
globalThis.expect(identity._0.userId).toBe("u_0123");
|
|
303
|
+
});
|
|
304
|
+
globalThis.test("fromAppSyncIdentity: no claim is exactly the sub", () => {
|
|
305
|
+
let id_sub = "c3741234-aaaa";
|
|
306
|
+
let id_claims = {};
|
|
307
|
+
let id = {
|
|
308
|
+
sub: id_sub,
|
|
309
|
+
claims: id_claims
|
|
310
|
+
};
|
|
311
|
+
let identity = Auth_Cognito$ReventlessAws.fromAppSyncIdentity(id);
|
|
312
|
+
if (typeof identity !== "object") {
|
|
313
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
314
|
+
}
|
|
315
|
+
if (identity.TAG !== "Authenticated") {
|
|
316
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
317
|
+
}
|
|
318
|
+
globalThis.expect(identity._0.userId).toBe("c3741234-aaaa");
|
|
319
|
+
});
|
|
320
|
+
globalThis.test("an empty claim is treated as absent", () => {
|
|
321
|
+
let id_sub = "c3741234-aaaa";
|
|
322
|
+
let id_claims = Object.fromEntries([[
|
|
323
|
+
Auth_UserId$ReventlessCore.claim,
|
|
324
|
+
""
|
|
325
|
+
]]);
|
|
326
|
+
let id = {
|
|
327
|
+
sub: id_sub,
|
|
328
|
+
claims: id_claims
|
|
329
|
+
};
|
|
330
|
+
let identity = Auth_Cognito$ReventlessAws.fromAppSyncIdentity(id);
|
|
331
|
+
if (typeof identity !== "object") {
|
|
332
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
333
|
+
}
|
|
334
|
+
if (identity.TAG !== "Authenticated") {
|
|
335
|
+
return Stdlib_JsError.throwWithMessage("expected Authenticated");
|
|
336
|
+
}
|
|
337
|
+
globalThis.expect(identity._0.userId).toBe("c3741234-aaaa");
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
243
341
|
export {
|
|
244
342
|
buildContext,
|
|
245
343
|
b64urlEncode,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// The identity store's name and key, shared by the deploy, the trigger and the
|
|
4
|
+
// provisioning scripts. The symptom of any two disagreeing is quiet: the claim
|
|
5
|
+
// is never added, and every new account silently keeps its `sub`.
|
|
6
|
+
|
|
7
|
+
module Schema = Auth_IdentityStore_Schema
|
|
8
|
+
|
|
9
|
+
describe("Auth_IdentityStore_Schema", () => {
|
|
10
|
+
testSync("names the store after the provider whose trigger reads it", () =>
|
|
11
|
+
expect(Schema.derivedStoreName(~identityProviderId="eu-west-1_CQTwafSeX"))->toBe(
|
|
12
|
+
"ReventlessIdentityStore-eu-west-1_CQTwafSeX",
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
testSync("does not collide with the active-role store on the same provider", () =>
|
|
17
|
+
expect(
|
|
18
|
+
Schema.derivedStoreName(~identityProviderId="eu-west-1_A") ==
|
|
19
|
+
Auth_ActiveRoleStore_Schema.derivedStoreName(~identityProviderId="eu-west-1_A"),
|
|
20
|
+
)->toBe(false)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
testSync("is keyed on the subject alone", () =>
|
|
24
|
+
expect(Schema.expectedKeySchema)->toEqual([("id", "HASH")])
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
testSync("adopts a table with the right key and refuses one with another", () => {
|
|
28
|
+
expect(Schema.keySchemaRefusal(~tableName="t", ~actual=[("id", "HASH")]))->toEqual(None)
|
|
29
|
+
expect(
|
|
30
|
+
Schema.keySchemaRefusal(~tableName="t", ~actual=[("id", "HASH"), ("clientId", "RANGE")])
|
|
31
|
+
->Option.map(m => m->String.includes("identity store needs [id:HASH]"))
|
|
32
|
+
->Option.getOr(false),
|
|
33
|
+
)->toBe(true)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Auth_IdentityStore_Schema$ReventlessAws from "../src/adapter/Auth/Auth_IdentityStore_Schema.res.mjs";
|
|
5
|
+
import * as Auth_ActiveRoleStore_Schema$ReventlessAws from "../src/adapter/Auth/Auth_ActiveRoleStore_Schema.res.mjs";
|
|
6
|
+
|
|
7
|
+
globalThis.describe("Auth_IdentityStore_Schema", () => {
|
|
8
|
+
globalThis.test("names the store after the provider whose trigger reads it", () => {
|
|
9
|
+
globalThis.expect(Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName("eu-west-1_CQTwafSeX")).toBe("ReventlessIdentityStore-eu-west-1_CQTwafSeX");
|
|
10
|
+
});
|
|
11
|
+
globalThis.test("does not collide with the active-role store on the same provider", () => {
|
|
12
|
+
globalThis.expect(Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName("eu-west-1_A") === Auth_ActiveRoleStore_Schema$ReventlessAws.derivedStoreName("eu-west-1_A")).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
globalThis.test("is keyed on the subject alone", () => {
|
|
15
|
+
globalThis.expect(Auth_IdentityStore_Schema$ReventlessAws.expectedKeySchema).toEqual([[
|
|
16
|
+
"id",
|
|
17
|
+
"HASH"
|
|
18
|
+
]]);
|
|
19
|
+
});
|
|
20
|
+
globalThis.test("adopts a table with the right key and refuses one with another", () => {
|
|
21
|
+
globalThis.expect(Auth_IdentityStore_Schema$ReventlessAws.keySchemaRefusal("t", [[
|
|
22
|
+
"id",
|
|
23
|
+
"HASH"
|
|
24
|
+
]])).toEqual(undefined);
|
|
25
|
+
globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.map(Auth_IdentityStore_Schema$ReventlessAws.keySchemaRefusal("t", [
|
|
26
|
+
[
|
|
27
|
+
"id",
|
|
28
|
+
"HASH"
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
"clientId",
|
|
32
|
+
"RANGE"
|
|
33
|
+
]
|
|
34
|
+
]), m => m.includes("identity store needs [id:HASH]")), false)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
let Schema;
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
Schema,
|
|
42
|
+
}
|
|
43
|
+
/* Not a pure module */
|