@reventlessdev/reventless-aws 3.0.0-alpha.369 → 3.0.0-alpha.371
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 +11 -11
- package/scripts/ProvisionAdmin.res +2 -2
- package/scripts/ProvisionAdmin.res.mjs +7 -6
- package/src/Platform.res +56 -7
- package/src/Platform.res.mjs +46 -7
- package/src/Platform_ProvidedGroups.res +97 -0
- package/src/Platform_ProvidedGroups.res.mjs +103 -0
- package/src/Platform_Stack.res +6 -3
- package/src/Platform_Stack.res.mjs +3 -3
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +1 -1
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +1 -1
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +1 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +1 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +2 -1
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +1 -0
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +1 -0
- package/src/components/Api/AppSync_Adapter.res +4 -3
- package/src/components/Api/AppSync_SdlDecorate.res +7 -3
- package/src/components/Api/AppSync_SdlDecorate.res.mjs +4 -3
- package/src/util/Util_OwnerScopeEnv.res +9 -0
- package/src/util/Util_OwnerScopeEnv.res.mjs +14 -1
- package/src/util/Util_ShellConfig.res +4 -0
- package/src/util/Util_ShellConfig.res.mjs +5 -1
- package/tests/AppSync_AdapterTest.res +46 -23
- package/tests/AppSync_AdapterTest.res.mjs +35 -27
- package/tests/PgPipeline_IntegrationTest.res +2 -1
- package/tests/PgQueryDbUnion_IntegrationTest.res +2 -1
- package/tests/Platform_ProvidedGroupsTest.res +52 -0
- package/tests/Platform_ProvidedGroupsTest.res.mjs +98 -0
- package/tests/Util_OwnerScopeEnvTest.res +48 -2
- package/tests/Util_OwnerScopeEnvTest.res.mjs +49 -3
- package/tests/Util_ShellConfigTest.res +17 -0
- package/tests/Util_ShellConfigTest.res.mjs +18 -0
- package/tests/integration/AggTestAggregate.res +2 -1
- package/tests/integration/EpCompositeSlice.res +2 -1
- package/tests/integration/EpTestSlice.res +2 -1
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.371 (2026-10-01)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* check a plugin's roles against the user pool's groups ([ddb3c42](https://github.com/ReventlessDev/reventless-core/commit/ddb3c422b7226a19800e781abd5f03af18867007))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.370 (2026-10-01)
|
|
14
|
+
|
|
15
|
+
* feat!: roles a plugin declares, groups a platform provides ([43daace](https://github.com/ReventlessDev/reventless-core/commit/43daace6ca588afe22562d079af1e6f1da81942e))
|
|
16
|
+
|
|
17
|
+
### BREAKING CHANGES
|
|
18
|
+
|
|
19
|
+
* AllowGroups and AdminGroup are removed — write AllowRoles over
|
|
20
|
+
the plugin's Roles.t, and use Role.admin / Platform.adminGroup(). Spec module
|
|
21
|
+
types declare `type role`, and their rules are Authorization.rule<role>; a spec
|
|
22
|
+
that writes its own rule declares `type role` (Reventless.Role.name for a rule
|
|
23
|
+
over Authorization.permission). Role.name is a private string: make one from a
|
|
24
|
+
string with Role.make.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
# 3.0.0-alpha.369 (2026-10-01)
|
|
7
29
|
|
|
8
30
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.371",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -21,19 +21,19 @@
|
|
|
21
21
|
"sury": "11.0.0",
|
|
22
22
|
"uuid": "^13.0.0",
|
|
23
23
|
"yaml": "^2.8.3",
|
|
24
|
-
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.
|
|
25
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
26
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.20",
|
|
27
|
-
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.22",
|
|
24
|
+
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.28",
|
|
28
25
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
29
|
-
"@reventlessdev/rescript-
|
|
26
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.20",
|
|
27
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
28
|
+
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.23",
|
|
29
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
|
|
30
30
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
|
|
31
31
|
"@reventlessdev/rescript-web": "1.0.0-alpha.7",
|
|
32
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
33
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
34
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
35
|
-
"@reventlessdev/reventless-
|
|
36
|
-
"@reventlessdev/reventless-
|
|
32
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.185",
|
|
33
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.287",
|
|
34
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.151",
|
|
35
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.157",
|
|
36
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.39"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"rescript": "12.3.1",
|
|
@@ -76,7 +76,7 @@ Make the first administrator of a Reventless deployment.
|
|
|
76
76
|
one. The run always names the stack it used.
|
|
77
77
|
--email <address> The address they sign in with.
|
|
78
78
|
|
|
79
|
-
Creates the "${Reventless.
|
|
79
|
+
Creates the "${Reventless.Role.adminGroup()}" group if it is missing, the account if
|
|
80
80
|
it is missing, a working password, and the membership between them. Provisions
|
|
81
81
|
nothing that a platform stack owns. Region and credentials come from the
|
|
82
82
|
environment, as for any AWS SDK call.
|
|
@@ -222,7 +222,7 @@ let run = async ({email, providerId, stack}: request): result<unit, string> =>
|
|
|
222
222
|
switch await checkPoolAcceptsEmail(~providerId) {
|
|
223
223
|
| Error(_) as e => e
|
|
224
224
|
| Ok() =>
|
|
225
|
-
let group = Reventless.
|
|
225
|
+
let group = Reventless.Role.adminGroup()
|
|
226
226
|
let password = Reventless.Util_Password.generate()
|
|
227
227
|
await ensureGroup(~providerId, ~group)
|
|
228
228
|
await ensureUser(~providerId, ~email)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
4
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
4
5
|
import * as CliArgs$Reventless from "@reventlessdev/reventless-spec/src/CliArgs.res.mjs";
|
|
5
6
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
6
|
-
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
7
7
|
import * as Util_Password$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Password.res.mjs";
|
|
8
8
|
import * as Util_AwsError$ReventlessAws from "../src/util/Util_AwsError.res.mjs";
|
|
9
9
|
import * as ProvisionCognito$ReventlessAws from "./ProvisionCognito.res.mjs";
|
|
@@ -36,7 +36,7 @@ Make the first administrator of a Reventless deployment.
|
|
|
36
36
|
one. The run always names the stack it used.
|
|
37
37
|
--email <address> The address they sign in with.
|
|
38
38
|
|
|
39
|
-
Creates the "` +
|
|
39
|
+
Creates the "` + Role$Reventless.adminGroup() + `" group if it is missing, the account if
|
|
40
40
|
it is missing, a working password, and the membership between them. Provisions
|
|
41
41
|
nothing that a platform stack owns. Region and credentials come from the
|
|
42
42
|
environment, as for any AWS SDK call.
|
|
@@ -156,12 +156,13 @@ async function run(param) {
|
|
|
156
156
|
if (e$1.TAG !== "Ok") {
|
|
157
157
|
return e$1;
|
|
158
158
|
}
|
|
159
|
+
let group = Role$Reventless.adminGroup();
|
|
159
160
|
let password = Util_Password$Reventless.generate();
|
|
160
|
-
await ensureGroup(providerId,
|
|
161
|
+
await ensureGroup(providerId, group);
|
|
161
162
|
await ensureUser(providerId, email);
|
|
162
163
|
await setPassword(providerId, email, password);
|
|
163
|
-
await addToGroup(providerId, email,
|
|
164
|
-
console.log(signInDetails(providerId, email, password,
|
|
164
|
+
await addToGroup(providerId, email, group);
|
|
165
|
+
console.log(signInDetails(providerId, email, password, group));
|
|
165
166
|
return {
|
|
166
167
|
TAG: "Ok",
|
|
167
168
|
_0: undefined
|
|
@@ -212,4 +213,4 @@ export {
|
|
|
212
213
|
cli,
|
|
213
214
|
main,
|
|
214
215
|
}
|
|
215
|
-
/*
|
|
216
|
+
/* usage Not a pure module */
|
package/src/Platform.res
CHANGED
|
@@ -107,6 +107,47 @@ let messagingEmailSenderRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.ma
|
|
|
107
107
|
`""` means the platform named none, which the runtime reads as its default. */
|
|
108
108
|
let messagingEmailProviderRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make(""))
|
|
109
109
|
|
|
110
|
+
/** Which group stands for which role, only where the two differ; every other
|
|
111
|
+
role maps to the group of its own name. Call before `Make()`: the platform
|
|
112
|
+
resolves roles while it is built, and a mapping stated later is refused. */
|
|
113
|
+
let roleGroups = Reventless.Role.setGroups
|
|
114
|
+
|
|
115
|
+
/** Groups a root says its identity provider has, for a provider the platform
|
|
116
|
+
cannot list. Where it can (`loadProvidedGroups`), the listing decides, and a
|
|
117
|
+
declared group the pool lacks fails the deploy. */
|
|
118
|
+
let providedGroups = Reventless.Role.provideGroups
|
|
119
|
+
|
|
120
|
+
/** The platform stack a plugin stack deploys against (`platform:stack`), or
|
|
121
|
+
`None` in a program that is its own platform. One reference per program:
|
|
122
|
+
a second with the same name is a duplicate resource. */
|
|
123
|
+
let platformStackRefCache: ref<option<option<Pulumi.StackReference.t>>> = ref(None)
|
|
124
|
+
|
|
125
|
+
let platformStackReference = (): option<Pulumi.StackReference.t> =>
|
|
126
|
+
switch platformStackRefCache.contents {
|
|
127
|
+
| Some(stackRef) => stackRef
|
|
128
|
+
| None =>
|
|
129
|
+
let stackRef =
|
|
130
|
+
Pulumi.Config.make(Some("platform"))
|
|
131
|
+
->Pulumi.Config.get("stack")
|
|
132
|
+
->Option.map(stack => Pulumi.StackReference.make(stack))
|
|
133
|
+
platformStackRefCache := Some(stackRef)
|
|
134
|
+
stackRef
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Ask the user pool which groups it has, so the role check compares each
|
|
138
|
+
plugin's roles with the pool. A plugin's deploy program calls `deployPlugin`
|
|
139
|
+
once this resolves; see [Platform_ProvidedGroups]. */
|
|
140
|
+
let loadProvidedGroups = (): promise<unit> =>
|
|
141
|
+
Platform_ProvidedGroups.load(~stackRef=platformStackReference())
|
|
142
|
+
|
|
143
|
+
/** The group a role resolves to — the name this platform writes into its
|
|
144
|
+
directives, its shell configuration and the elevated groups. For code that
|
|
145
|
+
grants access by group, so it never spells one itself. */
|
|
146
|
+
let groupOf = Reventless.Role.groupOf
|
|
147
|
+
|
|
148
|
+
/** `groupOf(Role.admin)`. */
|
|
149
|
+
let adminGroup = Reventless.Role.adminGroup
|
|
150
|
+
|
|
110
151
|
module MakeWithConfig = (
|
|
111
152
|
Config: {
|
|
112
153
|
let splitApi: bool
|
|
@@ -144,7 +185,15 @@ module MakeWithConfig = (
|
|
|
144
185
|
// A default, never a forcing: an explicit `setElevatedGroups` in a platform
|
|
145
186
|
// root — including one naming nobody — means what it says and wins. See
|
|
146
187
|
// [Reventless.OwnerScope.defaultElevatedGroups].
|
|
147
|
-
Reventless.OwnerScope.
|
|
188
|
+
Reventless.OwnerScope.defaultElevatedRoles([Reventless.Role.admin])
|
|
189
|
+
|
|
190
|
+
// What this deployment provides for the roles its plugins need, where the pool
|
|
191
|
+
// was not listed (`loadProvidedGroups`): the administrator group the stack
|
|
192
|
+
// declares, and the groups of the accounts provisioning creates. Read when each
|
|
193
|
+
// plugin is built, after the root has mapped its roles.
|
|
194
|
+
Reventless.Role.provideGroupsFrom(() =>
|
|
195
|
+
[Reventless.Role.adminGroup()]->Array.concat(Reventless.AccountsManifest.declaredGroups())
|
|
196
|
+
)
|
|
148
197
|
|
|
149
198
|
// Dispatch per-flavor commandHandlerConfig records to the four runtime
|
|
150
199
|
// builders. Every sub-record is optional; only branches the caller actually
|
|
@@ -202,10 +251,7 @@ module MakeWithConfig = (
|
|
|
202
251
|
// Determine API source based on platform:stack config.
|
|
203
252
|
// - Platform/monolithic mode (no config): create a real AppSync API resource.
|
|
204
253
|
// - Plugin mode (config set): reference the platform's shared API via StackReference.
|
|
205
|
-
let platformStackRef =
|
|
206
|
-
Pulumi.Config.make(Some("platform"))
|
|
207
|
-
->Pulumi.Config.get("stack")
|
|
208
|
-
->Option.map(stack => Pulumi.StackReference.make(stack))
|
|
254
|
+
let platformStackRef = platformStackReference()
|
|
209
255
|
|
|
210
256
|
// The declared object stores, as the upload claimer needs them: qualified
|
|
211
257
|
// name, physical bucket, served prefix.
|
|
@@ -308,13 +354,16 @@ module MakeWithConfig = (
|
|
|
308
354
|
// Admin base as a source-API document — auth-decorated (all fields Admin,
|
|
309
355
|
// Cognito-only; the deploy-time SigV4 system-caller fields died with the
|
|
310
356
|
// fragment registry) plus the canonical stamp.
|
|
311
|
-
let adminSourceSdl = (): string =>
|
|
357
|
+
let adminSourceSdl = (): string => {
|
|
358
|
+
// The directive below fixes the administrator's group for this deploy.
|
|
359
|
+
Reventless.Role.freeze()
|
|
312
360
|
assembleCanonicalSourceSdl(
|
|
313
361
|
~baseFragment=AppSync_Adapter.injectAwsAuthAll(
|
|
314
362
|
ReventlessCore.Platform_AdminApi.baseFragment(~cloner=Config.cloner),
|
|
315
|
-
~group=Reventless.
|
|
363
|
+
~group=Reventless.Role.adminGroup(),
|
|
316
364
|
),
|
|
317
365
|
)
|
|
366
|
+
}
|
|
318
367
|
|
|
319
368
|
// Split-mode Domain source document: relay base types + Platform_ping — the
|
|
320
369
|
// Domain merged API's canonical owner (plugin fields come from plugin sources).
|
package/src/Platform.res.mjs
CHANGED
|
@@ -13,10 +13,10 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
13
13
|
import * as Pulumi from "@pulumi/pulumi";
|
|
14
14
|
import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
|
|
15
15
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
16
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
16
17
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
17
18
|
import * as Plugin$ReventlessAws from "./components/Plugin.res.mjs";
|
|
18
19
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
19
|
-
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
20
20
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
21
21
|
import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
|
|
22
22
|
import * as Plugin$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin.res.mjs";
|
|
@@ -34,6 +34,7 @@ import * as Util_Bundle$ReventlessAws from "./util/Util_Bundle.res.mjs";
|
|
|
34
34
|
import * as Plugin_Stack$ReventlessAws from "./plugin/stack/Plugin_Stack.res.mjs";
|
|
35
35
|
import * as UiFragments$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs";
|
|
36
36
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
37
|
+
import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
|
|
37
38
|
import * as Platform_Casts$ReventlessAws from "./Platform_Casts.res.mjs";
|
|
38
39
|
import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
|
|
39
40
|
import * as QueryDbBackend$ReventlessAws from "./adapter/QueryDb/QueryDbBackend.res.mjs";
|
|
@@ -79,6 +80,7 @@ import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/Eve
|
|
|
79
80
|
import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
|
|
80
81
|
import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
|
|
81
82
|
import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
|
|
83
|
+
import * as Platform_ProvidedGroups$ReventlessAws from "./Platform_ProvidedGroups.res.mjs";
|
|
82
84
|
import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments_Projection.res.mjs";
|
|
83
85
|
import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
|
|
84
86
|
import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
|
|
@@ -151,8 +153,27 @@ let messagingEmailProviderRef = {
|
|
|
151
153
|
contents: Pulumi.output("")
|
|
152
154
|
};
|
|
153
155
|
|
|
156
|
+
let platformStackRefCache = {
|
|
157
|
+
contents: undefined
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
function platformStackReference() {
|
|
161
|
+
let stackRef = platformStackRefCache.contents;
|
|
162
|
+
if (stackRef !== undefined) {
|
|
163
|
+
return Primitive_option.valFromOption(stackRef);
|
|
164
|
+
}
|
|
165
|
+
let stackRef$1 = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
|
|
166
|
+
platformStackRefCache.contents = Primitive_option.some(stackRef$1);
|
|
167
|
+
return stackRef$1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function loadProvidedGroups() {
|
|
171
|
+
return Platform_ProvidedGroups$ReventlessAws.load(platformStackReference());
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
function MakeWithConfig(Config) {
|
|
155
|
-
OwnerScope$Reventless.
|
|
175
|
+
OwnerScope$Reventless.defaultElevatedRoles([Role$Reventless.admin]);
|
|
176
|
+
Role$Reventless.provideGroupsFrom(() => [Role$Reventless.adminGroup()].concat(AccountsManifest$Reventless.declaredGroups()));
|
|
156
177
|
Stdlib_Option.forEach(Config.commandHandlerConfig.aggregates, param => {
|
|
157
178
|
Stdlib_Option.forEach(param.sync, AggregateRuntime_Builder_Single$ReventlessAws.setConfig);
|
|
158
179
|
Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
|
|
@@ -181,7 +202,7 @@ function MakeWithConfig(Config) {
|
|
|
181
202
|
let currentDeployTarget = {
|
|
182
203
|
contents: "Domain"
|
|
183
204
|
};
|
|
184
|
-
let platformStackRef =
|
|
205
|
+
let platformStackRef = platformStackReference();
|
|
185
206
|
let claimStores = () => {
|
|
186
207
|
if (platformStackRef !== undefined) {
|
|
187
208
|
return Primitive_option.valFromOption(platformStackRef).getOutput("objectStores").apply(objectStores => Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(objectStores, Stdlib_JSON.Decode.object), {})), param => {
|
|
@@ -214,7 +235,8 @@ function MakeWithConfig(Config) {
|
|
|
214
235
|
subscriptionSources: []
|
|
215
236
|
});
|
|
216
237
|
let adminSourceSdl = () => {
|
|
217
|
-
|
|
238
|
+
Role$Reventless.freeze();
|
|
239
|
+
let baseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(Platform_AdminApi$ReventlessCore.baseFragment(Config.cloner), Role$Reventless.adminGroup(), undefined);
|
|
218
240
|
return AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes(AppSync_Adapter$ReventlessAws.stitchStandaloneWithAwsDirectives(baseFragment));
|
|
219
241
|
};
|
|
220
242
|
let match;
|
|
@@ -1566,7 +1588,8 @@ function MakeWithConfig(Config) {
|
|
|
1566
1588
|
|
|
1567
1589
|
function Make($star) {
|
|
1568
1590
|
let commandHandlerConfig = {};
|
|
1569
|
-
OwnerScope$Reventless.
|
|
1591
|
+
OwnerScope$Reventless.defaultElevatedRoles([Role$Reventless.admin]);
|
|
1592
|
+
Role$Reventless.provideGroupsFrom(() => [Role$Reventless.adminGroup()].concat(AccountsManifest$Reventless.declaredGroups()));
|
|
1570
1593
|
Stdlib_Option.forEach(commandHandlerConfig.aggregates, param => {
|
|
1571
1594
|
Stdlib_Option.forEach(param.sync, AggregateRuntime_Builder_Single$ReventlessAws.setConfig);
|
|
1572
1595
|
Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
|
|
@@ -1595,7 +1618,7 @@ function Make($star) {
|
|
|
1595
1618
|
let currentDeployTarget = {
|
|
1596
1619
|
contents: "Domain"
|
|
1597
1620
|
};
|
|
1598
|
-
let platformStackRef =
|
|
1621
|
+
let platformStackRef = platformStackReference();
|
|
1599
1622
|
let claimStores = () => {
|
|
1600
1623
|
if (platformStackRef !== undefined) {
|
|
1601
1624
|
return Primitive_option.valFromOption(platformStackRef).getOutput("objectStores").apply(objectStores => Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(objectStores, Stdlib_JSON.Decode.object), {})), param => {
|
|
@@ -1628,7 +1651,8 @@ function Make($star) {
|
|
|
1628
1651
|
subscriptionSources: []
|
|
1629
1652
|
});
|
|
1630
1653
|
let adminSourceSdl = () => {
|
|
1631
|
-
|
|
1654
|
+
Role$Reventless.freeze();
|
|
1655
|
+
let baseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(Platform_AdminApi$ReventlessCore.baseFragment(false), Role$Reventless.adminGroup(), undefined);
|
|
1632
1656
|
return AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes(AppSync_Adapter$ReventlessAws.stitchStandaloneWithAwsDirectives(baseFragment));
|
|
1633
1657
|
};
|
|
1634
1658
|
let match;
|
|
@@ -2958,6 +2982,14 @@ function Make($star) {
|
|
|
2958
2982
|
};
|
|
2959
2983
|
}
|
|
2960
2984
|
|
|
2985
|
+
let roleGroups = Role$Reventless.setGroups;
|
|
2986
|
+
|
|
2987
|
+
let providedGroups = Role$Reventless.provideGroups;
|
|
2988
|
+
|
|
2989
|
+
let groupOf = Role$Reventless.groupOf;
|
|
2990
|
+
|
|
2991
|
+
let adminGroup = Role$Reventless.adminGroup;
|
|
2992
|
+
|
|
2961
2993
|
export {
|
|
2962
2994
|
log,
|
|
2963
2995
|
apiConfigRef,
|
|
@@ -2969,6 +3001,13 @@ export {
|
|
|
2969
3001
|
geocoderPlaceIndexRef,
|
|
2970
3002
|
messagingEmailSenderRef,
|
|
2971
3003
|
messagingEmailProviderRef,
|
|
3004
|
+
roleGroups,
|
|
3005
|
+
providedGroups,
|
|
3006
|
+
platformStackRefCache,
|
|
3007
|
+
platformStackReference,
|
|
3008
|
+
loadProvidedGroups,
|
|
3009
|
+
groupOf,
|
|
3010
|
+
adminGroup,
|
|
2972
3011
|
MakeWithConfig,
|
|
2973
3012
|
Make,
|
|
2974
3013
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/***
|
|
2
|
+
The user pool's groups, read before a plugin is built so the role check compares
|
|
3
|
+
its roles with the pool, not a manifest (`Role.provideListedGroups`). A group the
|
|
4
|
+
pool lacks refuses every caller, which nothing at runtime can tell from a caller
|
|
5
|
+
without the role. Works in a preview: `ListGroups` is a read, and the pool id is
|
|
6
|
+
read outside any `Output.apply`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
module Cognito = AwsSdk.CognitoIdentityServiceProvider
|
|
10
|
+
|
|
11
|
+
type pool = {id: string, region: option<string>}
|
|
12
|
+
|
|
13
|
+
type send = Cognito.ListGroupsCommand.input => promise<Cognito.ListGroupsCommand.output>
|
|
14
|
+
|
|
15
|
+
/** Every group of the pool, following `NextToken` across pages. */
|
|
16
|
+
let listAll = async (~send: send, ~userPoolId: string): array<string> => {
|
|
17
|
+
let rec page = async (nextToken, acc) => {
|
|
18
|
+
let out = await send({userPoolId, limit: 60, ?nextToken})
|
|
19
|
+
let names = out.groups->Option.getOr([])->Array.filterMap(g => g.groupName)
|
|
20
|
+
let acc = acc->Array.concat(names)
|
|
21
|
+
switch out.nextToken {
|
|
22
|
+
| Some(_) as next => await page(next, acc)
|
|
23
|
+
| None => acc
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
await page(None, [])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A plugin stack's pool, from the platform stack's exports. The new key before
|
|
30
|
+
the old one, and each directly before under `default`, where ESM programs
|
|
31
|
+
nest their exports. */
|
|
32
|
+
let poolFromExports = (~get: string => option<JSON.t>): option<pool> => {
|
|
33
|
+
let nested = get("default")->Option.flatMap(JSON.Decode.object)
|
|
34
|
+
let read = key =>
|
|
35
|
+
get(key)
|
|
36
|
+
->Option.orElse(nested->Option.flatMap(d => d->Dict.get(key)))
|
|
37
|
+
->Option.flatMap(JSON.Decode.string)
|
|
38
|
+
read("identityProviderId")
|
|
39
|
+
->Option.orElse(read("cognitoUserPoolId"))
|
|
40
|
+
->Option.map(id => {
|
|
41
|
+
id,
|
|
42
|
+
region: read("identityProviderRegion")->Option.orElse(read("cognitoRegion")),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
The pool this program deploys against, or `None` where it cannot be listed yet:
|
|
48
|
+
a pool this program creates does not exist before its first deploy.
|
|
49
|
+
|
|
50
|
+
A plugin stack reads its platform stack's exports, the same ones its resolvers
|
|
51
|
+
use. A program that is its own platform reads a supplied pool's id from config.
|
|
52
|
+
*/
|
|
53
|
+
let resolvePool = async (~stackRef: option<Pulumi.StackReference.t>): option<pool> =>
|
|
54
|
+
switch stackRef {
|
|
55
|
+
| Some(stackRef) =>
|
|
56
|
+
let keys = [
|
|
57
|
+
"identityProviderId",
|
|
58
|
+
"cognitoUserPoolId",
|
|
59
|
+
"identityProviderRegion",
|
|
60
|
+
"cognitoRegion",
|
|
61
|
+
"default",
|
|
62
|
+
]
|
|
63
|
+
let values = await Promise.all(
|
|
64
|
+
keys->Array.map(key => stackRef->Pulumi.StackReference.getOutputValue(key)),
|
|
65
|
+
)
|
|
66
|
+
let exports = Dict.fromArray(
|
|
67
|
+
keys->Array.mapWithIndex((key, i) => (key, values->Array.getUnsafe(i))),
|
|
68
|
+
)
|
|
69
|
+
poolFromExports(~get=key => exports->Dict.get(key)->Option.flatMap(v => v))
|
|
70
|
+
| None =>
|
|
71
|
+
Platform_Stack._identityProviderId(
|
|
72
|
+
~cfg=Pulumi.Config.make(Some("platform")),
|
|
73
|
+
)->Option.map(id => {
|
|
74
|
+
id,
|
|
75
|
+
region: Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region"),
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let sendTo = (~region: option<string>): send => {
|
|
80
|
+
let client = Cognito.Raw.client(~options={region: ?region})
|
|
81
|
+
input => Cognito.ListGroupsCommand.make(input)->Cognito.ListGroupsCommand.Raw.send(client, _)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** List the pool's groups and record them for the role check. Leaves the check
|
|
85
|
+
on its other sources where there is no pool to ask yet. */
|
|
86
|
+
let load = async (~stackRef: option<Pulumi.StackReference.t>): unit =>
|
|
87
|
+
switch await resolvePool(~stackRef) {
|
|
88
|
+
| None => ()
|
|
89
|
+
| Some({id, region}) =>
|
|
90
|
+
let groups = try await listAll(~send=sendTo(~region), ~userPoolId=id) catch {
|
|
91
|
+
| exn if exn->Util_AwsError.hasCode(~code="AccessDeniedException") =>
|
|
92
|
+
JsError.throwWithMessage(
|
|
93
|
+
`Cannot list the groups of the user pool ${id}: the deploying principal needs ` ++ `cognito-idp:ListGroups on it, so the deploy can check that every role a plugin needs has a group.`,
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
Reventless.Role.provideListedGroups(~source=`the user pool ${id}`, groups)
|
|
97
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
7
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
9
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
10
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
11
|
+
import * as Util_AwsError$ReventlessAws from "./util/Util_AwsError.res.mjs";
|
|
12
|
+
import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
|
|
13
|
+
import * as ClientCognitoIdentityProvider from "@aws-sdk/client-cognito-identity-provider";
|
|
14
|
+
|
|
15
|
+
async function listAll(send, userPoolId) {
|
|
16
|
+
let page = async (nextToken, acc) => {
|
|
17
|
+
let out = await send({
|
|
18
|
+
UserPoolId: userPoolId,
|
|
19
|
+
Limit: 60,
|
|
20
|
+
NextToken: nextToken
|
|
21
|
+
});
|
|
22
|
+
let names = Stdlib_Array.filterMap(Stdlib_Option.getOr(out.Groups, []), g => g.GroupName);
|
|
23
|
+
let acc$1 = acc.concat(names);
|
|
24
|
+
let next = out.NextToken;
|
|
25
|
+
if (next !== undefined) {
|
|
26
|
+
return await page(next, acc$1);
|
|
27
|
+
} else {
|
|
28
|
+
return acc$1;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return await page(undefined, []);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function poolFromExports(get) {
|
|
35
|
+
let nested = Stdlib_Option.flatMap(get("default"), Stdlib_JSON.Decode.object);
|
|
36
|
+
let read = key => Stdlib_Option.flatMap(Stdlib_Option.orElse(get(key), Stdlib_Option.flatMap(nested, d => d[key])), Stdlib_JSON.Decode.string);
|
|
37
|
+
return Stdlib_Option.map(Stdlib_Option.orElse(read("identityProviderId"), read("cognitoUserPoolId")), id => ({
|
|
38
|
+
id: id,
|
|
39
|
+
region: Stdlib_Option.orElse(read("identityProviderRegion"), read("cognitoRegion"))
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function resolvePool(stackRef) {
|
|
44
|
+
if (stackRef === undefined) {
|
|
45
|
+
return Stdlib_Option.map(Platform_Stack$ReventlessAws._identityProviderId(new Pulumi.Config("platform")), id => ({
|
|
46
|
+
id: id,
|
|
47
|
+
region: new Pulumi.Config("aws").get("region")
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
let stackRef$1 = Primitive_option.valFromOption(stackRef);
|
|
51
|
+
let keys = [
|
|
52
|
+
"identityProviderId",
|
|
53
|
+
"cognitoUserPoolId",
|
|
54
|
+
"identityProviderRegion",
|
|
55
|
+
"cognitoRegion",
|
|
56
|
+
"default"
|
|
57
|
+
];
|
|
58
|
+
let values = await Promise.all(keys.map(key => stackRef$1.getOutputValue(key)));
|
|
59
|
+
let $$exports = Object.fromEntries(keys.map((key, i) => [
|
|
60
|
+
key,
|
|
61
|
+
values[i]
|
|
62
|
+
]));
|
|
63
|
+
return poolFromExports(key => Stdlib_Option.flatMap($$exports[key], v => v));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sendTo(region) {
|
|
67
|
+
let client = new ClientCognitoIdentityProvider.CognitoIdentityProviderClient({
|
|
68
|
+
region: region
|
|
69
|
+
});
|
|
70
|
+
return input => client.send(new ClientCognitoIdentityProvider.ListGroupsCommand(input));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function load(stackRef) {
|
|
74
|
+
let match = await resolvePool(stackRef);
|
|
75
|
+
if (match === undefined) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
let id = match.id;
|
|
79
|
+
let groups;
|
|
80
|
+
try {
|
|
81
|
+
groups = await listAll(sendTo(match.region), id);
|
|
82
|
+
} catch (raw_exn) {
|
|
83
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
84
|
+
if (Util_AwsError$ReventlessAws.hasCode(exn, "AccessDeniedException")) {
|
|
85
|
+
groups = Stdlib_JsError.throwWithMessage(`Cannot list the groups of the user pool ` + id + `: the deploying principal needs cognito-idp:ListGroups on it, so the deploy can check that every role a plugin needs has a group.`);
|
|
86
|
+
} else {
|
|
87
|
+
throw exn;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return Role$Reventless.provideListedGroups(`the user pool ` + id, groups);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let Cognito;
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
Cognito,
|
|
97
|
+
listAll,
|
|
98
|
+
poolFromExports,
|
|
99
|
+
resolvePool,
|
|
100
|
+
sendTo,
|
|
101
|
+
load,
|
|
102
|
+
}
|
|
103
|
+
/* @pulumi/pulumi Not a pure module */
|
package/src/Platform_Stack.res
CHANGED
|
@@ -25,7 +25,7 @@ type cognitoUserPool = {
|
|
|
25
25
|
* with SPA-friendly defaults (email sign-in unless `platform:loginIdentifier`
|
|
26
26
|
* says otherwise, 12-char password policy, no MFA, and admin-only user
|
|
27
27
|
* creation unless `platform:signUpMode` says otherwise), plus the
|
|
28
|
-
* administrator group named by [Reventless.
|
|
28
|
+
* administrator group named by [Reventless.Role.adminGroup] and an elevated-groups
|
|
29
29
|
* default naming it. What it does **not** create is the first administrator:
|
|
30
30
|
* accounts are not stack resources, and a pool full of them is not something a
|
|
31
31
|
* `pulumi destroy` should be able to empty. `provision-admin` makes that one,
|
|
@@ -302,7 +302,7 @@ let _resolveUncached = (): cognitoUserPool => {
|
|
|
302
302
|
// An ordinary child resource: the pool is ours, so this needs no out-of-band
|
|
303
303
|
// call and Pulumi's state stays the authority for whether it exists.
|
|
304
304
|
let _adminGroup = Util_CognitoGroupUser.addUserGroup(
|
|
305
|
-
~name=Reventless.
|
|
305
|
+
~name=Reventless.Role.adminGroup(),
|
|
306
306
|
~userPoolId=pool.id,
|
|
307
307
|
)
|
|
308
308
|
|
|
@@ -400,7 +400,10 @@ let _resolveUncached = (): cognitoUserPool => {
|
|
|
400
400
|
// name is what a caller needs either way, and an output that appeared in one
|
|
401
401
|
// mode and not the other would read as "there is no administrator group here"
|
|
402
402
|
// rather than "this stack did not create it".
|
|
403
|
-
Pulumi.Pulumi.export(
|
|
403
|
+
Pulumi.Pulumi.export(
|
|
404
|
+
"identityProviderAdminGroup",
|
|
405
|
+
Pulumi.Output.make(Reventless.Role.adminGroup()),
|
|
406
|
+
)
|
|
404
407
|
|
|
405
408
|
// Exported so an operator can read back the store this provider resolved to —
|
|
406
409
|
// the name is derived, not configured, so this is a convenience for checking
|
|
@@ -5,7 +5,7 @@ import * as Pulumi$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Pulumi
|
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Pulumi from "@pulumi/pulumi";
|
|
7
7
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
|
-
import * as
|
|
8
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
9
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
10
10
|
import * as AWS_Tags$ReventlessAws from "./adapter/AWS_Tags.res.mjs";
|
|
11
11
|
import * as Auth_SignUpMode$ReventlessAws from "./adapter/Auth/Auth_SignUpMode.res.mjs";
|
|
@@ -133,7 +133,7 @@ function _resolveUncached() {
|
|
|
133
133
|
mfaConfiguration: "OFF",
|
|
134
134
|
tags: AWS_Tags$ReventlessAws.make("HostUiPool", "Platform", "Auth", "Platform", undefined, undefined, undefined, undefined)
|
|
135
135
|
});
|
|
136
|
-
Util_CognitoGroupUser$ReventlessAws.addUserGroup(
|
|
136
|
+
Util_CognitoGroupUser$ReventlessAws.addUserGroup(Role$Reventless.adminGroup(), pool.id);
|
|
137
137
|
let tokenUnits2_accessToken = "minutes";
|
|
138
138
|
let tokenUnits2_idToken = "minutes";
|
|
139
139
|
let tokenUnits2_refreshToken = "days";
|
|
@@ -181,7 +181,7 @@ function _resolveUncached() {
|
|
|
181
181
|
Pulumi$Pulumi.$$export("cognitoUserPoolArn", result.poolArn);
|
|
182
182
|
Pulumi$Pulumi.$$export("cognitoRegion", regionOutput);
|
|
183
183
|
Pulumi$Pulumi.$$export("cognitoUserPoolManaged", managedStr);
|
|
184
|
-
Pulumi$Pulumi.$$export("identityProviderAdminGroup", Pulumi.output(
|
|
184
|
+
Pulumi$Pulumi.$$export("identityProviderAdminGroup", Pulumi.output(Role$Reventless.adminGroup()));
|
|
185
185
|
Pulumi$Pulumi.$$export("activeRoleStore", result.activeRoleTable.name);
|
|
186
186
|
return result;
|
|
187
187
|
}
|
|
@@ -194,7 +194,7 @@ let make = (
|
|
|
194
194
|
let adminEntryJson =
|
|
195
195
|
ReventlessCore.Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
196
196
|
~pluginId=ReventlessCore.Platform_Admin_Structure.pluginId,
|
|
197
|
-
ReventlessCore.Platform_Admin_Structure.structure,
|
|
197
|
+
ReventlessCore.Platform_Admin_Structure.structure(),
|
|
198
198
|
)->JSON.stringify
|
|
199
199
|
|
|
200
200
|
// Bundle reventless-aws (the compiled `_Ops` handler lives inside it) and
|
|
@@ -107,7 +107,7 @@ function make(api, pluginReadModelTableName, offloadBucketName, schemaReady, bak
|
|
|
107
107
|
encodeSelections(j.selections)
|
|
108
108
|
]
|
|
109
109
|
]))) : "";
|
|
110
|
-
let adminEntryJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(Platform_Admin_Structure$ReventlessCore.pluginId, undefined, Platform_Admin_Structure$ReventlessCore.structure));
|
|
110
|
+
let adminEntryJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(Platform_Admin_Structure$ReventlessCore.pluginId, undefined, Platform_Admin_Structure$ReventlessCore.structure()));
|
|
111
111
|
let packageDirs = Object.fromEntries([[
|
|
112
112
|
"@reventlessdev/reventless-aws",
|
|
113
113
|
Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// ([Auth_ActiveRoleTrigger.res] bundles it and attaches it to the user pool).
|
|
7
7
|
//
|
|
8
8
|
// The narrowing has to happen here rather than in our own enforcement code
|
|
9
|
-
// because `@authorize(
|
|
9
|
+
// because `@authorize(AllowRoles([...]))` compiles to a field directive AppSync
|
|
10
10
|
// evaluates against `cognito:groups` **before any of our code executes**. A
|
|
11
11
|
// request header we invent could scope reads correctly and still leave every
|
|
12
12
|
// group-gated mutation callable — right about the data, wrong about the writes.
|
|
@@ -150,7 +150,7 @@ let registerBinding = (
|
|
|
150
150
|
),
|
|
151
151
|
labelField: entry.labelField,
|
|
152
152
|
includeIdParam: entry.includeIdParam,
|
|
153
|
-
authorization: spec.authorization,
|
|
153
|
+
authorization: spec.authorization->Reventless.Authorization.named,
|
|
154
154
|
// From the same schema `capability` is derived from, one line above, so the
|
|
155
155
|
// two cannot end up disagreeing about this read model's fields.
|
|
156
156
|
ownerField: Reventless.Owner.fieldNames(spec.stateSchema)->Array.get(0),
|