@reventlessdev/reventless-spec 3.0.0-alpha.154 → 3.0.0-alpha.156
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 +1 -1
- package/src/components/Aggregate.res +4 -1
- package/src/components/InboundTranslationSlice.res +4 -1
- package/src/components/Owner.res +30 -0
- package/src/components/Owner.res.mjs +36 -0
- package/src/components/Plugin.res +8 -0
- package/src/components/Plugin.res.mjs +5 -2
- package/src/components/ReadModel.res +4 -1
- package/src/components/StateChangeSlice.res +4 -1
- package/src/components/StateViewSlice.res +4 -1
- package/src/lifecycle/CheckLifecycleModel.res +3 -0
- package/src/lifecycle/CheckLifecycleModel.res.mjs +3 -0
- package/src/types/AccountsManifest.res +23 -1
- package/src/types/AccountsManifest.res.mjs +18 -0
- package/src/types/Authorization.res +37 -10
- package/src/types/Authorization.res.mjs +29 -15
- package/src/types/DeploymentProvenance.res +84 -0
- package/src/types/DeploymentProvenance.res.mjs +86 -0
- package/src/types/Environments.res +230 -0
- package/src/types/Environments.res.mjs +314 -0
- package/src/types/Message.res +21 -0
- package/src/types/Message.res.mjs +16 -1
- package/src/types/OwnerScope.res +113 -6
- package/src/types/OwnerScope.res.mjs +110 -3
- package/src/types/Role.res +143 -0
- package/src/types/Role.res.mjs +129 -0
- package/src/types/RoleCoverage.res +84 -0
- package/src/types/RoleCoverage.res.mjs +72 -0
- package/src/types/authorizationNamed.mjs +17 -0
- package/src/types/AdminGroup.res +0 -27
- package/src/types/AdminGroup.res.mjs +0 -9
|
@@ -0,0 +1,72 @@
|
|
|
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 Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
import * as Role$Reventless from "./Role.res.mjs";
|
|
6
|
+
import * as OwnerScope$Reventless from "./OwnerScope.res.mjs";
|
|
7
|
+
|
|
8
|
+
function needsOf(structure, plugin) {
|
|
9
|
+
let rolesOf = (item, roles) => Stdlib_Option.getOr(roles, []).map(role => ({
|
|
10
|
+
plugin: plugin,
|
|
11
|
+
item: item,
|
|
12
|
+
role: role,
|
|
13
|
+
group: Role$Reventless.groupOf(role)
|
|
14
|
+
}));
|
|
15
|
+
let writable = defs => defs.flatMap(w => w.commands.flatMap(c => rolesOf(w.name + `.` + c.name, c.requiredRoles)));
|
|
16
|
+
let queryable = defs => defs.flatMap(q => rolesOf(q.name, q.requiredRoles));
|
|
17
|
+
return [
|
|
18
|
+
writable(structure.aggregates),
|
|
19
|
+
writable(structure.stateChangeSlices),
|
|
20
|
+
structure.inboundTranslationSlices.flatMap(s => rolesOf(s.name, s.requiredRoles)),
|
|
21
|
+
queryable(structure.readModels),
|
|
22
|
+
queryable(structure.stateViewSlices)
|
|
23
|
+
].flat();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function elevatedNeeds(roles) {
|
|
27
|
+
return roles.map(role => ({
|
|
28
|
+
plugin: "platform",
|
|
29
|
+
item: "elevated roles",
|
|
30
|
+
role: role,
|
|
31
|
+
group: Role$Reventless.groupOf(role)
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function unmet(needs, provided) {
|
|
36
|
+
return needs.filter(n => !provided.includes(n.group));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function describe(n) {
|
|
40
|
+
let role = n.role;
|
|
41
|
+
if (n.group === role) {
|
|
42
|
+
return n.plugin + `: ` + n.item + ` needs the role ` + role;
|
|
43
|
+
} else {
|
|
44
|
+
return n.plugin + `: ` + n.item + ` needs the role ` + role + ` (mapped to the group ` + n.group + `)`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function unmetMessage(missing, provided) {
|
|
49
|
+
return `Roles this platform cannot provide — every caller would be refused:\n` + missing.map(n => ` ` + describe(n)).join("\n") + (`\n The platform provides the groups: ` + provided.join(", ") + `.\n`) + ` Add an account in that group to the accounts manifest (.reventless/users.yaml), map the role to a group that exists (Platform.roleGroups), or declare the groups of a user pool this deployment did not create (Platform.providedGroups).`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function check(structure, plugin) {
|
|
53
|
+
let provided = Role$Reventless.providedGroups();
|
|
54
|
+
if (provided === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let elevated = Stdlib_Option.getOr(OwnerScope$Reventless.explicitElevatedRoles.contents, []);
|
|
58
|
+
let missing = unmet(needsOf(structure, plugin).concat(elevatedNeeds(elevated)), provided);
|
|
59
|
+
if (missing.length !== 0) {
|
|
60
|
+
return Stdlib_JsError.throwWithMessage(unmetMessage(missing, provided));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
needsOf,
|
|
66
|
+
elevatedNeeds,
|
|
67
|
+
unmet,
|
|
68
|
+
describe,
|
|
69
|
+
unmetMessage,
|
|
70
|
+
check,
|
|
71
|
+
}
|
|
72
|
+
/* Role-Reventless Not a pure module */
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// `Authorization.named`: a plugin's rule, whose roles are its own `Roles.t`
|
|
2
|
+
// cases, as the framework's rule over role names. A payload-less case is its
|
|
3
|
+
// name at run time, so the rule is returned as it is once every role has been
|
|
4
|
+
// checked to be one; anything else would be compared with group names and
|
|
5
|
+
// never match, so it is refused here instead.
|
|
6
|
+
export function named(rule) {
|
|
7
|
+
if (rule !== null && typeof rule === "object" && rule.TAG === "AllowRoles") {
|
|
8
|
+
for (const role of rule._0) {
|
|
9
|
+
if (typeof role !== "string") {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Authorization: a role must be a case of a plugin's Roles.t without a payload, got ${JSON.stringify(role)}`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return rule;
|
|
17
|
+
}
|
package/src/types/AdminGroup.res
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
The group whose members administer a deployment.
|
|
3
|
-
|
|
4
|
-
One definition, because the name is decided **twice and independently**: an
|
|
5
|
-
authorization rule names it to allow a read, and [OwnerScope.elevatedGroups]
|
|
6
|
-
names it to exempt a caller from `@owner` narrowing. Nothing checks that the two
|
|
7
|
-
agree, and a deployment where they disagree is refused nowhere — the
|
|
8
|
-
administrator passes every authorization check and then reads *empty views*,
|
|
9
|
-
because every owner-scoped view filters them out. Empty-because-scoped and
|
|
10
|
-
empty-because-there-is-nothing look identical from outside.
|
|
11
|
-
|
|
12
|
-
In `spec` rather than in a provider package because **both platforms hard-code
|
|
13
|
-
it**: the AWS platform decorates its admin API with it, while the local platform
|
|
14
|
-
both wraps its `Platform_*` fields in it and hands it to the built-in developer
|
|
15
|
-
account. A constant only one of them could reach would leave the other exactly as
|
|
16
|
-
divergent as it is today.
|
|
17
|
-
|
|
18
|
-
🚨 **This buys agreement, not the freedom to rename.** `"Admin"` is written into
|
|
19
|
-
every example's `@@reventless.authorize` annotations, and a PPX annotation takes a
|
|
20
|
-
literal — so the consumers that matter most *cannot* read this constant. Changing
|
|
21
|
-
the value here would leave those annotations behind and silently split the two
|
|
22
|
-
mechanisms this exists to hold together, which is the failure above rather than a
|
|
23
|
-
compile error. Renaming the administrator group is a different and much larger
|
|
24
|
-
act. `AdminGroupTest` asserts that the constant and the platform's own annotation
|
|
25
|
-
still say the same thing, because that boundary can carry no other check.
|
|
26
|
-
*/
|
|
27
|
-
let name = "Admin"
|