@reventlessdev/reventless-local 3.0.0-alpha.274 → 3.0.0-alpha.276
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 +23 -0
- package/package.json +9 -9
- package/src/Platform.res +30 -1
- package/src/Platform.res.mjs +30 -2
- package/src/ShellConfig.res +8 -1
- package/src/ShellConfig.res.mjs +6 -1
- package/src/adapter/Auth/LocalAuth.res +20 -5
- package/src/adapter/Auth/LocalAuth.res.mjs +26 -22
- package/src/adapter/PlatformGraphQL_Server.res +1 -1
- package/src/adapter/PlatformGraphQL_Server.res.mjs +2 -2
- package/src/reexport/CommandTopic.res.mjs +3 -0
- package/tests/ShellConfigTest.res +31 -0
- package/tests/ShellConfigTest.res.mjs +26 -0
- package/tests/adapter/CommandAuthorizationTest.res +27 -3
- package/tests/adapter/CommandAuthorizationTest.res.mjs +23 -1
- package/tests/adapter/QueryDbListResolverTest.res +4 -2
- package/tests/adapter/QueryDbResolvedFieldsTest.res +4 -2
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +6 -2
- package/tests/plugin/PluginBehavior_GWT.res.mjs +4 -2
- package/tests/plugin/UiFragmentRegistry_GWT.res.mjs +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
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.276 (2026-10-01)
|
|
7
|
+
|
|
8
|
+
* feat!: roles a plugin declares, groups a platform provides ([43daace](https://github.com/ReventlessDev/reventless-core/commit/43daace6ca588afe22562d079af1e6f1da81942e))
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* AllowGroups and AdminGroup are removed — write AllowRoles over
|
|
13
|
+
the plugin's Roles.t, and use Role.admin / Platform.adminGroup(). Spec module
|
|
14
|
+
types declare `type role`, and their rules are Authorization.rule<role>; a spec
|
|
15
|
+
that writes its own rule declares `type role` (Reventless.Role.name for a rule
|
|
16
|
+
over Authorization.permission). Role.name is a private string: make one from a
|
|
17
|
+
string with Role.make.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# 3.0.0-alpha.275 (2026-10-01)
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **spec, aws, core, local:** an app declares its environments, and every deployment records its commit ([c7cc925](https://github.com/ReventlessDev/reventless-core/commit/c7cc92573e97742b681a9000cea0bdfd6e9a33fc))
|
|
26
|
+
* **spec, core, gwt, ppx:** a command acts only on what its caller owns ([2636f0e](https://github.com/ReventlessDev/reventless-core/commit/2636f0ee44797ee2c0b0c40a7107cf306113173f))
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
# 3.0.0-alpha.274 (2026-09-29)
|
|
7
30
|
|
|
8
31
|
**Note:** Version bump only for package @reventlessdev/reventless-local
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.276",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
38
38
|
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.29",
|
|
39
39
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
40
|
+
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.23",
|
|
40
41
|
"@reventlessdev/rescript-node": "2.0.0-alpha.20",
|
|
41
42
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.22",
|
|
42
|
-
"@reventlessdev/
|
|
43
|
-
"@reventlessdev/reventless-
|
|
44
|
-
"@reventlessdev/reventless-
|
|
45
|
-
"@reventlessdev/reventless-
|
|
46
|
-
"@reventlessdev/reventless-
|
|
47
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.182",
|
|
43
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.233",
|
|
44
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.286",
|
|
45
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.134",
|
|
46
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.184",
|
|
47
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.156",
|
|
48
48
|
"@reventlessdev/reventless-seed": "1.0.0-alpha.31",
|
|
49
|
-
"@reventlessdev/reventless-
|
|
49
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.150"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.1",
|
|
53
53
|
"sury-ppx": "11.0.0",
|
|
54
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
54
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.96"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"rescript": "^12.3.1"
|
package/src/Platform.res
CHANGED
|
@@ -73,6 +73,24 @@ let decodeUiFragmentRegistryEventEnvelope = (eventJson: JSON.t): option<
|
|
|
73
73
|
// splitApi=true to serve core and plugin APIs on separate ports,
|
|
74
74
|
// backend=Backend.Sqlite({...}) to opt into file-backed SQLite persistence.
|
|
75
75
|
// Usage: module Platform = Platform.MakeWithConfig({let silent = true; let splitApi = false; let cloner = false; let backend = Backend.Memory})
|
|
76
|
+
/** Which group stands for which role, only where the two differ; every other
|
|
77
|
+
role maps to the group of its own name. Call before `Make()`: the platform
|
|
78
|
+
resolves roles while it is built, and a mapping stated later is refused. */
|
|
79
|
+
let roleGroups = Reventless.Role.setGroups
|
|
80
|
+
|
|
81
|
+
/** The groups of a user pool this deployment did not create, which no accounts
|
|
82
|
+
manifest names. They count as provided by the check that every role a plugin
|
|
83
|
+
needs has a group. */
|
|
84
|
+
let providedGroups = Reventless.Role.provideGroups
|
|
85
|
+
|
|
86
|
+
/** The group a role resolves to — the name this platform writes into its
|
|
87
|
+
directives, its shell configuration and the elevated groups. For code that
|
|
88
|
+
grants access by group, so it never spells one itself. */
|
|
89
|
+
let groupOf = Reventless.Role.groupOf
|
|
90
|
+
|
|
91
|
+
/** `groupOf(Role.admin)`. */
|
|
92
|
+
let adminGroup = Reventless.Role.adminGroup
|
|
93
|
+
|
|
76
94
|
module MakeWithConfig = (
|
|
77
95
|
Config: {
|
|
78
96
|
let silent: bool
|
|
@@ -87,6 +105,14 @@ module MakeWithConfig = (
|
|
|
87
105
|
// …) are no-ops in-memory. envVars are not propagated here because the
|
|
88
106
|
// local platform has no per-component process boundary.
|
|
89
107
|
let _ = Config.commandHandlerConfig
|
|
108
|
+
|
|
109
|
+
// What this platform provides for the roles its plugins need: every account it
|
|
110
|
+
// can sign in as, built-in and manifest alike. The manifest is read here rather
|
|
111
|
+
// than from the user store, which loads only when the servers start — after
|
|
112
|
+
// the plugins are built and checked.
|
|
113
|
+
Reventless.Role.provideGroupsFrom(() =>
|
|
114
|
+
LocalAuth.knownGroups()->Array.concat(Reventless.AccountsManifest.declaredGroups())
|
|
115
|
+
)
|
|
90
116
|
// Activate Pulumi mock mode — must happen before any component creation.
|
|
91
117
|
// Idempotent, so safe to call even if TestRunner.setup() was already called.
|
|
92
118
|
let _ = TestRunner.setup()
|
|
@@ -1273,6 +1299,7 @@ module MakeWithConfig = (
|
|
|
1273
1299
|
~pluginOutputs=?,
|
|
1274
1300
|
) => {
|
|
1275
1301
|
let environment = Pulumi.Pulumi.getStackName()
|
|
1302
|
+
let provenance = Reventless.DeploymentProvenance.current()
|
|
1276
1303
|
builtInfos->Array.forEachWithIndex((info, i) => {
|
|
1277
1304
|
switch ReventlessCore.Plugin_Helpers.onPluginDeployedHook.contents {
|
|
1278
1305
|
| Some(hook) =>
|
|
@@ -1289,6 +1316,8 @@ module MakeWithConfig = (
|
|
|
1289
1316
|
deployedAt: Date.make()->Date.toISOString,
|
|
1290
1317
|
actor: "local",
|
|
1291
1318
|
deploymentId: Date.make()->Date.toISOString,
|
|
1319
|
+
commit: ?provenance.commit,
|
|
1320
|
+
dirty: ?(provenance.dirty ? Some(true) : None),
|
|
1292
1321
|
kind: ?info.kind,
|
|
1293
1322
|
displayName: ?info.displayName,
|
|
1294
1323
|
vendor: ?info.vendor,
|
|
@@ -1786,7 +1815,7 @@ module MakeWithConfig = (
|
|
|
1786
1815
|
// structure manually.
|
|
1787
1816
|
pluginStructuresStore.contents->Dict.set(
|
|
1788
1817
|
ReventlessCore.Platform_Admin_Structure.pluginId,
|
|
1789
|
-
ReventlessCore.Platform_Admin_Structure.structure,
|
|
1818
|
+
ReventlessCore.Platform_Admin_Structure.structure(),
|
|
1790
1819
|
)
|
|
1791
1820
|
|
|
1792
1821
|
let pluginQueryDbName = ReventlessCore.PluginsReadModelSpec.name
|
package/src/Platform.res.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
12
12
|
import * as Effect from "effect/Effect";
|
|
13
13
|
import * as Pulumi from "@pulumi/pulumi";
|
|
14
14
|
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
15
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
15
16
|
import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
|
|
16
17
|
import * as AnsiStyle$Reventless from "@reventlessdev/reventless-spec/src/AnsiStyle.res.mjs";
|
|
17
18
|
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
@@ -25,10 +26,12 @@ import * as UiSlots$ReventlessLocal from "./UiSlots.res.mjs";
|
|
|
25
26
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
26
27
|
import * as LocalBus$ReventlessLocal from "./adapter/LocalBus.res.mjs";
|
|
27
28
|
import * as Api_Naming$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/Api_Naming.res.mjs";
|
|
29
|
+
import * as LocalAuth$ReventlessLocal from "./adapter/Auth/LocalAuth.res.mjs";
|
|
28
30
|
import * as PluginSpec$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginSpec.res.mjs";
|
|
29
31
|
import * as UserStore$ReventlessLocal from "./adapter/Auth/UserStore.res.mjs";
|
|
30
32
|
import * as TestRunner$ReventlessLocal from "./test/TestRunner.res.mjs";
|
|
31
33
|
import * as UiFragments$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs";
|
|
34
|
+
import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
|
|
32
35
|
import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
|
|
33
36
|
import * as EffectLogger$ReventlessCore from "@reventlessdev/reventless-core/src/util/EffectLogger.res.mjs";
|
|
34
37
|
import * as ShellConfig$ReventlessLocal from "./ShellConfig.res.mjs";
|
|
@@ -43,6 +46,7 @@ import * as PluginBehavior$ReventlessCore from "@reventlessdev/reventless-core/s
|
|
|
43
46
|
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
44
47
|
import * as Plugin_Builder$ReventlessLocal from "./components/Plugin_Builder.res.mjs";
|
|
45
48
|
import * as Counter_Builder$ReventlessLocal from "./components/Counter_Builder.res.mjs";
|
|
49
|
+
import * as DeploymentProvenance$Reventless from "@reventlessdev/reventless-spec/src/types/DeploymentProvenance.res.mjs";
|
|
46
50
|
import * as GraphQL_Stitcher$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
47
51
|
import * as LocalPluginSpec$ReventlessLocal from "./adapter/LocalPluginSpec.res.mjs";
|
|
48
52
|
import * as NoEventMappings$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/NoEventMappings.res.mjs";
|
|
@@ -139,6 +143,7 @@ function decodeUiFragmentRegistryEventEnvelope(eventJson) {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
function MakeWithConfig(Config) {
|
|
146
|
+
Role$Reventless.provideGroupsFrom(() => LocalAuth$ReventlessLocal.knownGroups().concat(AccountsManifest$Reventless.declaredGroups()));
|
|
142
147
|
TestRunner$ReventlessLocal.setup();
|
|
143
148
|
let match = Config.backend;
|
|
144
149
|
if (typeof match !== "object") {
|
|
@@ -1069,6 +1074,7 @@ function MakeWithConfig(Config) {
|
|
|
1069
1074
|
}));
|
|
1070
1075
|
let firePluginDeployedHooks = (builtInfos, pluginOutputs) => {
|
|
1071
1076
|
let environment = Pulumi.getStack();
|
|
1077
|
+
let provenance = DeploymentProvenance$Reventless.current();
|
|
1072
1078
|
builtInfos.forEach((info, i) => {
|
|
1073
1079
|
let hook = Plugin_Helpers$ReventlessCore.onPluginDeployedHook.contents;
|
|
1074
1080
|
if (hook === undefined) {
|
|
@@ -1081,6 +1087,8 @@ function MakeWithConfig(Config) {
|
|
|
1081
1087
|
let deployedInfo_version = info.version;
|
|
1082
1088
|
let deployedInfo_deployedAt = new Date().toISOString();
|
|
1083
1089
|
let deployedInfo_deploymentId = new Date().toISOString();
|
|
1090
|
+
let deployedInfo_commit = provenance.commit;
|
|
1091
|
+
let deployedInfo_dirty = provenance.dirty ? true : undefined;
|
|
1084
1092
|
let deployedInfo_kind = info.kind;
|
|
1085
1093
|
let deployedInfo_displayName = info.displayName;
|
|
1086
1094
|
let deployedInfo_vendor = info.vendor;
|
|
@@ -1100,6 +1108,8 @@ function MakeWithConfig(Config) {
|
|
|
1100
1108
|
deployedAt: deployedInfo_deployedAt,
|
|
1101
1109
|
actor: "local",
|
|
1102
1110
|
deploymentId: deployedInfo_deploymentId,
|
|
1111
|
+
commit: deployedInfo_commit,
|
|
1112
|
+
dirty: deployedInfo_dirty,
|
|
1103
1113
|
kind: deployedInfo_kind,
|
|
1104
1114
|
displayName: deployedInfo_displayName,
|
|
1105
1115
|
vendor: deployedInfo_vendor,
|
|
@@ -1354,7 +1364,7 @@ function MakeWithConfig(Config) {
|
|
|
1354
1364
|
connectPlugin(plugins$1);
|
|
1355
1365
|
seedPluginStructuresStore(plugins$1);
|
|
1356
1366
|
}
|
|
1357
|
-
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure;
|
|
1367
|
+
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure();
|
|
1358
1368
|
CommandGeneratorResolvers_GraphQL$ReventlessLocal.setPluginStatusGate(field => {
|
|
1359
1369
|
let parts = field.split("_");
|
|
1360
1370
|
let pluginPrefix = Stdlib_Option.getOr(parts[0], "");
|
|
@@ -1899,6 +1909,7 @@ function MakeWithConfig(Config) {
|
|
|
1899
1909
|
|
|
1900
1910
|
function Make($star) {
|
|
1901
1911
|
let backend = Backend$ReventlessLocal.fromEnv();
|
|
1912
|
+
Role$Reventless.provideGroupsFrom(() => LocalAuth$ReventlessLocal.knownGroups().concat(AccountsManifest$Reventless.declaredGroups()));
|
|
1902
1913
|
TestRunner$ReventlessLocal.setup();
|
|
1903
1914
|
if (typeof backend !== "object") {
|
|
1904
1915
|
BackendState$ReventlessLocal.setMemory();
|
|
@@ -2822,6 +2833,7 @@ function Make($star) {
|
|
|
2822
2833
|
}));
|
|
2823
2834
|
let firePluginDeployedHooks = (builtInfos, pluginOutputs) => {
|
|
2824
2835
|
let environment = Pulumi.getStack();
|
|
2836
|
+
let provenance = DeploymentProvenance$Reventless.current();
|
|
2825
2837
|
builtInfos.forEach((info, i) => {
|
|
2826
2838
|
let hook = Plugin_Helpers$ReventlessCore.onPluginDeployedHook.contents;
|
|
2827
2839
|
if (hook === undefined) {
|
|
@@ -2834,6 +2846,8 @@ function Make($star) {
|
|
|
2834
2846
|
let deployedInfo_version = info.version;
|
|
2835
2847
|
let deployedInfo_deployedAt = new Date().toISOString();
|
|
2836
2848
|
let deployedInfo_deploymentId = new Date().toISOString();
|
|
2849
|
+
let deployedInfo_commit = provenance.commit;
|
|
2850
|
+
let deployedInfo_dirty = provenance.dirty ? true : undefined;
|
|
2837
2851
|
let deployedInfo_kind = info.kind;
|
|
2838
2852
|
let deployedInfo_displayName = info.displayName;
|
|
2839
2853
|
let deployedInfo_vendor = info.vendor;
|
|
@@ -2853,6 +2867,8 @@ function Make($star) {
|
|
|
2853
2867
|
deployedAt: deployedInfo_deployedAt,
|
|
2854
2868
|
actor: "local",
|
|
2855
2869
|
deploymentId: deployedInfo_deploymentId,
|
|
2870
|
+
commit: deployedInfo_commit,
|
|
2871
|
+
dirty: deployedInfo_dirty,
|
|
2856
2872
|
kind: deployedInfo_kind,
|
|
2857
2873
|
displayName: deployedInfo_displayName,
|
|
2858
2874
|
vendor: deployedInfo_vendor,
|
|
@@ -3102,7 +3118,7 @@ function Make($star) {
|
|
|
3102
3118
|
connectPlugin(plugins$1);
|
|
3103
3119
|
seedPluginStructuresStore(plugins$1);
|
|
3104
3120
|
}
|
|
3105
|
-
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure;
|
|
3121
|
+
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure();
|
|
3106
3122
|
CommandGeneratorResolvers_GraphQL$ReventlessLocal.setPluginStatusGate(field => {
|
|
3107
3123
|
let parts = field.split("_");
|
|
3108
3124
|
let pluginPrefix = Stdlib_Option.getOr(parts[0], "");
|
|
@@ -3631,6 +3647,14 @@ function Make($star) {
|
|
|
3631
3647
|
};
|
|
3632
3648
|
}
|
|
3633
3649
|
|
|
3650
|
+
let roleGroups = Role$Reventless.setGroups;
|
|
3651
|
+
|
|
3652
|
+
let providedGroups = Role$Reventless.provideGroups;
|
|
3653
|
+
|
|
3654
|
+
let groupOf = Role$Reventless.groupOf;
|
|
3655
|
+
|
|
3656
|
+
let adminGroup = Role$Reventless.adminGroup;
|
|
3657
|
+
|
|
3634
3658
|
export {
|
|
3635
3659
|
log,
|
|
3636
3660
|
platformGraphQLRef,
|
|
@@ -3638,6 +3662,10 @@ export {
|
|
|
3638
3662
|
platformMCPRef,
|
|
3639
3663
|
decodePluginEventEnvelope,
|
|
3640
3664
|
decodeUiFragmentRegistryEventEnvelope,
|
|
3665
|
+
roleGroups,
|
|
3666
|
+
providedGroups,
|
|
3667
|
+
groupOf,
|
|
3668
|
+
adminGroup,
|
|
3641
3669
|
MakeWithConfig,
|
|
3642
3670
|
Make,
|
|
3643
3671
|
}
|
package/src/ShellConfig.res
CHANGED
|
@@ -41,7 +41,12 @@ let manifestUrlOf = (config: ReventlessInfra.Platform.bakedManifest): string =>
|
|
|
41
41
|
// caller matching no declared group gets and what every existing deployment has.
|
|
42
42
|
let journeyManifestsKey = "journeyManifestUrls"
|
|
43
43
|
|
|
44
|
-
let computedKeys = [
|
|
44
|
+
let computedKeys = [
|
|
45
|
+
"manifestUrl",
|
|
46
|
+
journeyManifestsKey,
|
|
47
|
+
ReventlessCore.Platform_UiSlots.configKey,
|
|
48
|
+
ReventlessCore.Platform_AdminGroup.configKey,
|
|
49
|
+
]
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
52
|
The overlay this platform puts on top of the shipped `config.json`.
|
|
@@ -69,6 +74,8 @@ let overlay = (
|
|
|
69
74
|
)
|
|
70
75
|
)
|
|
71
76
|
|
|
77
|
+
ReventlessCore.Platform_AdminGroup.configEntry()->Option.forEach(((k, v)) => out->Dict.set(k, v))
|
|
78
|
+
|
|
72
79
|
bakedManifest->Option.forEach(config => {
|
|
73
80
|
out->Dict.set("manifestUrl", JSON.Encode.string(manifestUrlOf(config)))
|
|
74
81
|
// Omitted entirely when nothing is declared, so a single-audience deployment
|
package/src/ShellConfig.res.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
9
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
10
10
|
import * as HostShellDist$ReventlessLocal from "./HostShellDist.res.mjs";
|
|
11
11
|
import * as Platform_UiSlots$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_UiSlots.res.mjs";
|
|
12
|
+
import * as Platform_AdminGroup$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_AdminGroup.res.mjs";
|
|
12
13
|
import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
|
|
13
14
|
|
|
14
15
|
let log = Logger$ReventlessCore.fromEnv();
|
|
@@ -26,7 +27,8 @@ let journeyManifestsKey = "journeyManifestUrls";
|
|
|
26
27
|
let computedKeys = [
|
|
27
28
|
"manifestUrl",
|
|
28
29
|
journeyManifestsKey,
|
|
29
|
-
Platform_UiSlots$ReventlessCore.configKey
|
|
30
|
+
Platform_UiSlots$ReventlessCore.configKey,
|
|
31
|
+
Platform_AdminGroup$ReventlessCore.configKey
|
|
30
32
|
];
|
|
31
33
|
|
|
32
34
|
function overlay(bakedManifest, uiSlotsFile, shellConfig) {
|
|
@@ -34,6 +36,9 @@ function overlay(bakedManifest, uiSlotsFile, shellConfig) {
|
|
|
34
36
|
Stdlib_Option.forEach(uiSlotsFile, param => {
|
|
35
37
|
out[Platform_UiSlots$ReventlessCore.configKey] = Platform_UiSlots$ReventlessCore.url;
|
|
36
38
|
});
|
|
39
|
+
Stdlib_Option.forEach(Platform_AdminGroup$ReventlessCore.configEntry(), param => {
|
|
40
|
+
out[param[0]] = param[1];
|
|
41
|
+
});
|
|
37
42
|
Stdlib_Option.forEach(bakedManifest, config => {
|
|
38
43
|
out["manifestUrl"] = Platform_BakedManifest$ReventlessCore.urlForKey(config.key);
|
|
39
44
|
let urls = Platform_BakedManifest$ReventlessCore.journeyUrls(config);
|
|
@@ -21,19 +21,22 @@ let defaultUser: Identity.t = {
|
|
|
21
21
|
provider: InMemory,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
// A function, not a value: the administrator's group is the one the platform
|
|
25
|
+
// root maps `Role.admin` to, and this module is evaluated before the root runs.
|
|
26
|
+
let adminUser = (): Identity.t => {
|
|
25
27
|
userId: "local-admin",
|
|
26
28
|
username: "admin",
|
|
27
|
-
groups: [
|
|
29
|
+
groups: [Role.adminGroup(), "User"],
|
|
28
30
|
provider: InMemory,
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// ── User registry ─────────────────────────────────────────────────────────
|
|
32
34
|
//
|
|
33
35
|
// A `dict<Identity.t>` keyed by `username`. A4 hydrates this from
|
|
34
|
-
// `.reventless/users.yaml`; tests inject via `registerUser`.
|
|
36
|
+
// `.reventless/users.yaml`; tests inject via `registerUser`. The built-in
|
|
37
|
+
// `admin` is answered on lookup, for the reason above, unless one is registered.
|
|
35
38
|
|
|
36
|
-
let initialUsers = () => Dict.fromArray([("user", defaultUser)
|
|
39
|
+
let initialUsers = () => Dict.fromArray([("user", defaultUser)])
|
|
37
40
|
|
|
38
41
|
let users: ref<dict<Identity.t>> = ref(initialUsers())
|
|
39
42
|
|
|
@@ -42,7 +45,19 @@ let registerUser = (~username: string, ~identity: Identity.t): unit =>
|
|
|
42
45
|
|
|
43
46
|
let resetUsers = (): unit => users := initialUsers()
|
|
44
47
|
|
|
45
|
-
let lookupUser = (username: string): option<Identity.t> =>
|
|
48
|
+
let lookupUser = (username: string): option<Identity.t> =>
|
|
49
|
+
switch users.contents->Dict.get(username) {
|
|
50
|
+
| None if username == "admin" => Some(adminUser())
|
|
51
|
+
| found => found
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The groups of every account this adapter can authenticate as, built-in
|
|
55
|
+
included — what the local platform provides. */
|
|
56
|
+
let knownGroups = (): array<string> =>
|
|
57
|
+
users.contents
|
|
58
|
+
->Dict.valuesToArray
|
|
59
|
+
->Array.concat([adminUser()])
|
|
60
|
+
->Array.flatMap(identity => identity.groups)
|
|
46
61
|
|
|
47
62
|
// ── Header parsing ────────────────────────────────────────────────────────
|
|
48
63
|
|
|
@@ -6,9 +6,9 @@ import * as Nodepath from "node:path";
|
|
|
6
6
|
import * as Nodecrypto from "node:crypto";
|
|
7
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
8
|
import * as Pulumi from "@pulumi/pulumi";
|
|
9
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
9
10
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
10
11
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
11
|
-
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
12
12
|
import * as Auth_ActiveRole$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_ActiveRole.res.mjs";
|
|
13
13
|
|
|
14
14
|
let defaultUser_groups = ["User"];
|
|
@@ -20,29 +20,23 @@ let defaultUser = {
|
|
|
20
20
|
provider: "InMemory"
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
23
|
+
function adminUser() {
|
|
24
|
+
return {
|
|
25
|
+
userId: "local-admin",
|
|
26
|
+
username: "admin",
|
|
27
|
+
groups: [
|
|
28
|
+
Role$Reventless.adminGroup(),
|
|
29
|
+
"User"
|
|
30
|
+
],
|
|
31
|
+
provider: "InMemory"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
function initialUsers() {
|
|
36
|
-
return Object.fromEntries([
|
|
37
|
-
[
|
|
36
|
+
return Object.fromEntries([[
|
|
38
37
|
"user",
|
|
39
38
|
defaultUser
|
|
40
|
-
]
|
|
41
|
-
[
|
|
42
|
-
"admin",
|
|
43
|
-
adminUser
|
|
44
|
-
]
|
|
45
|
-
]);
|
|
39
|
+
]]);
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
let users = {
|
|
@@ -58,7 +52,16 @@ function resetUsers() {
|
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
function lookupUser(username) {
|
|
61
|
-
|
|
55
|
+
let found = users.contents[username];
|
|
56
|
+
if (found !== undefined || username !== "admin") {
|
|
57
|
+
return found;
|
|
58
|
+
} else {
|
|
59
|
+
return adminUser();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function knownGroups() {
|
|
64
|
+
return Object.values(users.contents).concat([adminUser()]).flatMap(identity => identity.groups);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
function parseGroups(s) {
|
|
@@ -329,7 +332,7 @@ async function authenticate(ctx) {
|
|
|
329
332
|
let userHeader = ctx.headers["X-User".toLowerCase()];
|
|
330
333
|
let groupsHeader = ctx.headers["X-Groups".toLowerCase()];
|
|
331
334
|
if (userHeader !== undefined) {
|
|
332
|
-
let identity$1 =
|
|
335
|
+
let identity$1 = lookupUser(userHeader);
|
|
333
336
|
if (identity$1 === undefined) {
|
|
334
337
|
return "Anonymous";
|
|
335
338
|
}
|
|
@@ -371,6 +374,7 @@ export {
|
|
|
371
374
|
registerUser,
|
|
372
375
|
resetUsers,
|
|
373
376
|
lookupUser,
|
|
377
|
+
knownGroups,
|
|
374
378
|
parseGroups,
|
|
375
379
|
getHeader,
|
|
376
380
|
Login,
|
|
@@ -41,7 +41,7 @@ let wrapAdmin = (resolvers: dict<ReventlessGraphqlServer.GraphQL_ServerInstance.
|
|
|
41
41
|
resolvers
|
|
42
42
|
->Dict.toArray
|
|
43
43
|
->Array.forEach(((k, v)) =>
|
|
44
|
-
wrapped->Dict.set(k, Auth_GraphqlContext.requireGroup(~group=Reventless.
|
|
44
|
+
wrapped->Dict.set(k, Auth_GraphqlContext.requireGroup(~group=Reventless.Role.adminGroup(), v))
|
|
45
45
|
)
|
|
46
46
|
wrapped
|
|
47
47
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
4
4
|
import * as Auth_GraphqlContext$ReventlessLocal from "./Auth/Auth_GraphqlContext.res.mjs";
|
|
5
5
|
import * as GraphQL_ServerInstance$ReventlessGraphqlServer from "@reventlessdev/reventless-graphql-server/src/GraphQL_ServerInstance.res.mjs";
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ let instance = GraphQL_ServerInstance$ReventlessGraphqlServer.make("GraphQL:Plat
|
|
|
9
9
|
function wrapAdmin(resolvers) {
|
|
10
10
|
let wrapped = {};
|
|
11
11
|
Object.entries(resolvers).forEach(param => {
|
|
12
|
-
wrapped[param[0]] = Auth_GraphqlContext$ReventlessLocal.requireGroup(
|
|
12
|
+
wrapped[param[0]] = Auth_GraphqlContext$ReventlessLocal.requireGroup(Role$Reventless.adminGroup(), param[1]);
|
|
13
13
|
});
|
|
14
14
|
return wrapped;
|
|
15
15
|
}
|
|
@@ -18,6 +18,8 @@ let acceptedResultChannel = CommandTopic$ReventlessCore.acceptedResultChannel;
|
|
|
18
18
|
|
|
19
19
|
let rejectedResultChannel = CommandTopic$ReventlessCore.rejectedResultChannel;
|
|
20
20
|
|
|
21
|
+
let ownershipRefusal = CommandTopic$ReventlessCore.ownershipRefusal;
|
|
22
|
+
|
|
21
23
|
let reportAccepted = CommandTopic$ReventlessCore.reportAccepted;
|
|
22
24
|
|
|
23
25
|
let reportRejected = CommandTopic$ReventlessCore.reportRejected;
|
|
@@ -51,6 +53,7 @@ export {
|
|
|
51
53
|
fireCommandOutcome,
|
|
52
54
|
acceptedResultChannel,
|
|
53
55
|
rejectedResultChannel,
|
|
56
|
+
ownershipRefusal,
|
|
54
57
|
reportAccepted,
|
|
55
58
|
reportRejected,
|
|
56
59
|
commandOutcomeToJson,
|
|
@@ -268,3 +268,34 @@ describe("ShellConfig.overlay — journeys", () => {
|
|
|
268
268
|
)->toBe(true)
|
|
269
269
|
)
|
|
270
270
|
})
|
|
271
|
+
|
|
272
|
+
describe("ShellConfig.overlay — adminGroup", () => {
|
|
273
|
+
testSync("is absent while the administrator role keeps its own name", () => {
|
|
274
|
+
Reventless.Role.clearGroups()
|
|
275
|
+
expect(
|
|
276
|
+
ShellConfig.overlay(~bakedManifest=None, ~shellConfig=None)->Dict.keysToArray,
|
|
277
|
+
)->toEqual([])
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
testSync("carries the group a renamed administrator role maps to", () => {
|
|
281
|
+
Reventless.Role.clearGroups()
|
|
282
|
+
Reventless.Role.setGroups([(Reventless.Role.admin, "shop-admins")])
|
|
283
|
+
let out = ShellConfig.overlay(~bakedManifest=None, ~shellConfig=None)
|
|
284
|
+
Reventless.Role.clearGroups()
|
|
285
|
+
expect(out->str("adminGroup"))->toEqual(Some("shop-admins"))
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Computed from the role mapping, so a passthrough cannot point the shell at
|
|
289
|
+
// another group than the one the server gates on.
|
|
290
|
+
testSync("a shellConfig naming it is refused", () =>
|
|
291
|
+
expect(
|
|
292
|
+
threw(
|
|
293
|
+
() =>
|
|
294
|
+
ShellConfig.overlay(
|
|
295
|
+
~bakedManifest=None,
|
|
296
|
+
~shellConfig=Some(Dict.fromArray([("adminGroup", JSON.Encode.string("x"))])),
|
|
297
|
+
)->ignore,
|
|
298
|
+
),
|
|
299
|
+
)->toBe(true)
|
|
300
|
+
)
|
|
301
|
+
})
|
|
@@ -5,6 +5,7 @@ import * as Nodeos from "node:os";
|
|
|
5
5
|
import * as Nodepath from "node:path";
|
|
6
6
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
7
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
8
9
|
import * as TestRunner$ReventlessLocal from "../src/test/TestRunner.res.mjs";
|
|
9
10
|
import * as ShellConfig$ReventlessLocal from "../src/ShellConfig.res.mjs";
|
|
10
11
|
|
|
@@ -215,6 +216,31 @@ globalThis.describe("ShellConfig.overlay — journeys", () => {
|
|
|
215
216
|
});
|
|
216
217
|
});
|
|
217
218
|
|
|
219
|
+
globalThis.describe("ShellConfig.overlay — adminGroup", () => {
|
|
220
|
+
globalThis.test("is absent while the administrator role keeps its own name", () => {
|
|
221
|
+
Role$Reventless.clearGroups();
|
|
222
|
+
globalThis.expect(Object.keys(ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined))).toEqual([]);
|
|
223
|
+
});
|
|
224
|
+
globalThis.test("carries the group a renamed administrator role maps to", () => {
|
|
225
|
+
Role$Reventless.clearGroups();
|
|
226
|
+
Role$Reventless.setGroups([[
|
|
227
|
+
Role$Reventless.admin,
|
|
228
|
+
"shop-admins"
|
|
229
|
+
]]);
|
|
230
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
231
|
+
Role$Reventless.clearGroups();
|
|
232
|
+
globalThis.expect(str(out, "adminGroup")).toEqual("shop-admins");
|
|
233
|
+
});
|
|
234
|
+
globalThis.test("a shellConfig naming it is refused", () => {
|
|
235
|
+
globalThis.expect(threw(() => {
|
|
236
|
+
ShellConfig$ReventlessLocal.overlay(undefined, undefined, Object.fromEntries([[
|
|
237
|
+
"adminGroup",
|
|
238
|
+
"x"
|
|
239
|
+
]]));
|
|
240
|
+
})).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
218
244
|
export {
|
|
219
245
|
manifest,
|
|
220
246
|
str,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Resolver-boundary tests for per-constructor authorization (A3.3 + A3.3b).
|
|
2
2
|
// Mirrors the catalog Category aggregate's command shape: a record-payload
|
|
3
3
|
// constructor `Add({name})` defaulting to AllowAuthenticated, plus a
|
|
4
|
-
// payload-less `Archive` constrained to
|
|
4
|
+
// payload-less `Archive` constrained to AllowRoles([Admin]).
|
|
5
5
|
//
|
|
6
6
|
// The PPX emits a `commandAuthorization` switch at compile time (see
|
|
7
7
|
// examples/online-shop-hybrid/catalog/.../Category.res.mjs). This test hand-
|
|
@@ -23,11 +23,11 @@ type command =
|
|
|
23
23
|
| Rename({name: string})
|
|
24
24
|
| Archive
|
|
25
25
|
|
|
26
|
-
// Matches the shape the PPX emits for `@authorize(
|
|
26
|
+
// Matches the shape the PPX emits for `@authorize(AllowRoles([Admin]))` on
|
|
27
27
|
// the payload-less `Archive` constructor with a file-level default.
|
|
28
28
|
let commandAuthorization = (cmd: unknown): Reventless.Authorization.permission =>
|
|
29
29
|
if (cmd->Obj.magic: 'a) === "Archive" {
|
|
30
|
-
|
|
30
|
+
AllowRoles([Reventless.Role.make("Admin")])
|
|
31
31
|
} else {
|
|
32
32
|
AllowAuthenticated
|
|
33
33
|
}
|
|
@@ -189,6 +189,30 @@ describe("CommandGeneratorResolvers_GraphQL — per-constructor authorization",
|
|
|
189
189
|
expect(calls.contents)->toEqual(["Add"])
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
+
// The rule names the role; the resolver checks the group the platform maps it
|
|
193
|
+
// to, so a deployment that calls its administrators "ops" admits those.
|
|
194
|
+
testPromise("a renamed role admits its group and refuses the old name", async () => {
|
|
195
|
+
let (_, _, archiveResolver, calls) = buildFixture(~namespace="Cat8")
|
|
196
|
+
Reventless.Role.clearGroups()
|
|
197
|
+
Reventless.Role.setGroups([(Reventless.Role.make("Admin"), "ops")])
|
|
198
|
+
let asOps = await archiveResolver(
|
|
199
|
+
JSON.Encode.null,
|
|
200
|
+
JSON.Encode.object(Dict.make()),
|
|
201
|
+
ctxFor({...userIdentity, groups: ["ops"]}),
|
|
202
|
+
)
|
|
203
|
+
let asAdminGroup = await archiveResolver(
|
|
204
|
+
JSON.Encode.null,
|
|
205
|
+
JSON.Encode.object(Dict.make()),
|
|
206
|
+
ctxFor(adminIdentity),
|
|
207
|
+
)
|
|
208
|
+
Reventless.Role.clearGroups()
|
|
209
|
+
expect((getTypename(asOps), getErrorCode(asAdminGroup)))->toEqual((
|
|
210
|
+
"CommandAccepted",
|
|
211
|
+
"Forbidden",
|
|
212
|
+
))
|
|
213
|
+
expect(calls.contents)->toEqual(["Archive"])
|
|
214
|
+
})
|
|
215
|
+
|
|
192
216
|
testPromise(
|
|
193
217
|
"payload-less Archive registered as a GraphQL mutation field (no longer filtered)",
|
|
194
218
|
async () => {
|
|
@@ -6,6 +6,7 @@ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
|
6
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
8
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
9
|
+
import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
|
|
9
10
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
10
11
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
11
12
|
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
@@ -30,7 +31,7 @@ let commandSchema = Sury.union([
|
|
|
30
31
|
function commandAuthorization(cmd) {
|
|
31
32
|
if (cmd === "Archive") {
|
|
32
33
|
return {
|
|
33
|
-
TAG: "
|
|
34
|
+
TAG: "AllowRoles",
|
|
34
35
|
_0: ["Admin"]
|
|
35
36
|
};
|
|
36
37
|
} else {
|
|
@@ -170,6 +171,27 @@ globalThis.describe("CommandGeneratorResolvers_GraphQL — per-constructor autho
|
|
|
170
171
|
globalThis.expect(getTypename(response)).toEqual("CommandAccepted");
|
|
171
172
|
globalThis.expect(match[3].contents).toEqual(["Add"]);
|
|
172
173
|
});
|
|
174
|
+
globalThis.test("a renamed role admits its group and refuses the old name", async () => {
|
|
175
|
+
let match = buildFixture("Cat8");
|
|
176
|
+
let archiveResolver = match[2];
|
|
177
|
+
Role$Reventless.clearGroups();
|
|
178
|
+
Role$Reventless.setGroups([[
|
|
179
|
+
"Admin",
|
|
180
|
+
"ops"
|
|
181
|
+
]]);
|
|
182
|
+
let newrecord = {...userIdentity};
|
|
183
|
+
let asOps = await archiveResolver(null, {}, ctxFor((newrecord.groups = ["ops"], newrecord)));
|
|
184
|
+
let asAdminGroup = await archiveResolver(null, {}, ctxFor(adminIdentity));
|
|
185
|
+
Role$Reventless.clearGroups();
|
|
186
|
+
globalThis.expect([
|
|
187
|
+
getTypename(asOps),
|
|
188
|
+
getErrorCode(asAdminGroup)
|
|
189
|
+
]).toEqual([
|
|
190
|
+
"CommandAccepted",
|
|
191
|
+
"Forbidden"
|
|
192
|
+
]);
|
|
193
|
+
globalThis.expect(match[3].contents).toEqual(["Archive"]);
|
|
194
|
+
});
|
|
173
195
|
globalThis.test("payload-less Archive registered as a GraphQL mutation field (no longer filtered)", async () => {
|
|
174
196
|
buildFixture("Cat7");
|
|
175
197
|
let sdl = DomainGraphQL_Server$ReventlessLocal.asInterface.buildSdl();
|
|
@@ -145,7 +145,8 @@ let buildFixture = async (~name: string, ~indexes: array<Reventless.ReadModel.in
|
|
|
145
145
|
type state = rowState
|
|
146
146
|
let config = Reventless.ReadModel.config()
|
|
147
147
|
let subIdConfig = None
|
|
148
|
-
|
|
148
|
+
type role = Reventless.Role.name
|
|
149
|
+
let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
|
|
149
150
|
let visibility: Reventless.Visibility.t = Public
|
|
150
151
|
}
|
|
151
152
|
|
|
@@ -646,7 +647,8 @@ let buildSubIdFixture = async (~name: string) => {
|
|
|
646
647
|
Reventless.ReadModel.subIdField: "lineNo",
|
|
647
648
|
getSubId: (state: lineState) => state.lineNo,
|
|
648
649
|
})
|
|
649
|
-
|
|
650
|
+
type role = Reventless.Role.name
|
|
651
|
+
let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
|
|
650
652
|
let visibility: Reventless.Visibility.t = Public
|
|
651
653
|
}
|
|
652
654
|
|
|
@@ -96,7 +96,8 @@ let buildFixture = async (
|
|
|
96
96
|
type state = productRow
|
|
97
97
|
let config = Reventless.ReadModel.config()
|
|
98
98
|
let subIdConfig = None
|
|
99
|
-
|
|
99
|
+
type role = Reventless.Role.name
|
|
100
|
+
let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
|
|
100
101
|
let visibility: Reventless.Visibility.t = Public
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -125,7 +126,8 @@ let buildFixture = async (
|
|
|
125
126
|
],
|
|
126
127
|
)
|
|
127
128
|
let subIdConfig = None
|
|
128
|
-
|
|
129
|
+
type role = Reventless.Role.name
|
|
130
|
+
let authorization: Reventless.Authorization.rule<role> = AllowAuthenticated
|
|
129
131
|
let visibility: Reventless.Visibility.t = Public
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -222,7 +222,9 @@ describe("InboundTranslationSlice Callback", () => {
|
|
|
222
222
|
),
|
|
223
223
|
)
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
type role = Reventless.Role.name
|
|
226
|
+
|
|
227
|
+
let commandAuthorization = (_: command): Reventless.Authorization.rule<role> =>
|
|
226
228
|
AllowAuthenticated
|
|
227
229
|
type lifecycleState = unit
|
|
228
230
|
let commandTransition = (_: command): Reventless.Transition.t<lifecycleState> =>
|
|
@@ -289,7 +291,9 @@ describe("InboundTranslationSlice Callback", () => {
|
|
|
289
291
|
|
|
290
292
|
let translate = (_input: externalInput) => Ok([])
|
|
291
293
|
|
|
292
|
-
|
|
294
|
+
type role = Reventless.Role.name
|
|
295
|
+
|
|
296
|
+
let commandAuthorization = (_: command): Reventless.Authorization.rule<role> =>
|
|
293
297
|
AllowAuthenticated
|
|
294
298
|
type lifecycleState = unit
|
|
295
299
|
let commandTransition = (_: command): Reventless.Transition.t<lifecycleState> =>
|
|
@@ -9,13 +9,15 @@ let PluginTest = Behavior_GWT$ReventlessGwt.MakeFromAggregate({
|
|
|
9
9
|
name: PluginSpec$ReventlessCore.name,
|
|
10
10
|
commandSchema: PluginSpec$ReventlessCore.commandSchema,
|
|
11
11
|
eventSchema: PluginSpec$ReventlessCore.eventSchema,
|
|
12
|
-
errorSchema: PluginSpec$ReventlessCore.errorSchema
|
|
12
|
+
errorSchema: PluginSpec$ReventlessCore.errorSchema,
|
|
13
|
+
commandAuthorization: PluginSpec$ReventlessCore.commandAuthorization
|
|
13
14
|
})({
|
|
14
15
|
Spec: {
|
|
15
16
|
name: PluginSpec$ReventlessCore.name,
|
|
16
17
|
commandSchema: PluginSpec$ReventlessCore.commandSchema,
|
|
17
18
|
eventSchema: PluginSpec$ReventlessCore.eventSchema,
|
|
18
|
-
errorSchema: PluginSpec$ReventlessCore.errorSchema
|
|
19
|
+
errorSchema: PluginSpec$ReventlessCore.errorSchema,
|
|
20
|
+
commandAuthorization: PluginSpec$ReventlessCore.commandAuthorization
|
|
19
21
|
},
|
|
20
22
|
initialState: PluginBehavior$ReventlessCore.initialState,
|
|
21
23
|
evolve: PluginBehavior$ReventlessCore.evolve,
|
|
@@ -10,7 +10,8 @@ let Test = Behavior_GWT$ReventlessGwt.Make({
|
|
|
10
10
|
consumedEventSchema: UiFragmentRegistry$ReventlessCore.consumedEventSchema,
|
|
11
11
|
commandSchema: UiFragmentRegistry$ReventlessCore.commandSchema,
|
|
12
12
|
errorSchema: UiFragmentRegistry$ReventlessCore.errorSchema,
|
|
13
|
-
eventSchema: UiFragmentRegistry$ReventlessCore.eventSchema
|
|
13
|
+
eventSchema: UiFragmentRegistry$ReventlessCore.eventSchema,
|
|
14
|
+
commandAuthorization: UiFragmentRegistry$ReventlessCore.commandAuthorization
|
|
14
15
|
})({
|
|
15
16
|
initialState: undefined,
|
|
16
17
|
evolve: UiFragmentRegistry_Behavior$ReventlessCore.evolve,
|