@reventlessdev/reventless-aws 3.0.0-alpha.291 → 3.0.0-alpha.293
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 +15 -0
- package/package.json +9 -9
- package/src/Platform.res +85 -5
- package/src/Platform.res.mjs +62 -6
- package/src/Platform_Stack.res +78 -0
- package/src/Platform_Stack.res.mjs +17 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +8 -1
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +89 -12
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +55 -4
- package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +309 -0
- package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res.mjs +230 -0
- package/src/adapter/Auth/Auth_ActiveRoleStore.res +280 -0
- package/src/adapter/Auth/Auth_ActiveRoleStore.res.mjs +163 -0
- package/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res +148 -0
- package/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res.mjs +128 -0
- package/src/adapter/Auth/Auth_ActiveRoleTrigger.res +180 -0
- package/src/adapter/Auth/Auth_ActiveRoleTrigger.res.mjs +105 -0
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +201 -0
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res.mjs +126 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res +16 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +6 -0
- package/tests/Auth_ActiveRolePoolAttachmentTest.res +211 -0
- package/tests/Auth_ActiveRolePoolAttachmentTest.res.mjs +232 -0
- package/tests/Auth_ActiveRoleStoreTest.res +42 -0
- package/tests/Auth_ActiveRoleStoreTest.res.mjs +41 -0
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +169 -0
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res.mjs +189 -0
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +110 -0
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res.mjs +87 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
9
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
10
|
+
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
11
|
+
import * as IndexJs from "@pulumi/pulumi/dynamic/index.js";
|
|
12
|
+
|
|
13
|
+
let log = Logger$ReventlessCore.fromEnv();
|
|
14
|
+
|
|
15
|
+
let readOnlyKeys = [
|
|
16
|
+
"Id",
|
|
17
|
+
"Name",
|
|
18
|
+
"Arn",
|
|
19
|
+
"Status",
|
|
20
|
+
"CreationDate",
|
|
21
|
+
"LastModifiedDate",
|
|
22
|
+
"SchemaAttributes",
|
|
23
|
+
"AliasAttributes",
|
|
24
|
+
"UsernameAttributes",
|
|
25
|
+
"UsernameConfiguration",
|
|
26
|
+
"EstimatedNumberOfUsers",
|
|
27
|
+
"Domain",
|
|
28
|
+
"CustomDomain",
|
|
29
|
+
"SmsConfigurationFailure",
|
|
30
|
+
"EmailConfigurationFailure"
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function mergedUpdateInput(described, userPoolId, preTokenGenerationArn) {
|
|
34
|
+
let out = {};
|
|
35
|
+
Stdlib_Dict.forEachWithKey(described, (value, key) => {
|
|
36
|
+
if (!readOnlyKeys.includes(key)) {
|
|
37
|
+
out[key] = value;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
Stdlib_Option.forEach(described["Name"], name => {
|
|
42
|
+
out["PoolName"] = name;
|
|
43
|
+
});
|
|
44
|
+
out["UserPoolId"] = userPoolId;
|
|
45
|
+
let lambdaConfig = Stdlib_Option.mapOr(Stdlib_Option.flatMap(described["LambdaConfig"], Stdlib_JSON.Decode.object), {}, existing => Object.fromEntries(Object.entries(existing)));
|
|
46
|
+
if (preTokenGenerationArn !== undefined) {
|
|
47
|
+
lambdaConfig["PreTokenGeneration"] = preTokenGenerationArn;
|
|
48
|
+
} else {
|
|
49
|
+
Stdlib_Dict.$$delete(lambdaConfig, "PreTokenGeneration");
|
|
50
|
+
}
|
|
51
|
+
out["LambdaConfig"] = lambdaConfig;
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function attachedTrigger(described) {
|
|
56
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(described["LambdaConfig"], Stdlib_JSON.Decode.object), c => c["PreTokenGeneration"]), Stdlib_JSON.Decode.string);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let newOf1 = ((C, x) => new C(x));
|
|
60
|
+
|
|
61
|
+
let newOf0 = ((C) => new C());
|
|
62
|
+
|
|
63
|
+
let _sdk = {
|
|
64
|
+
contents: undefined
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let _client = {
|
|
68
|
+
contents: undefined
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
async function getSdk() {
|
|
72
|
+
let m = _sdk.contents;
|
|
73
|
+
if (m !== undefined) {
|
|
74
|
+
return m;
|
|
75
|
+
}
|
|
76
|
+
let m$1 = await import("@aws-sdk/client-cognito-identity-provider");
|
|
77
|
+
_sdk.contents = m$1;
|
|
78
|
+
return m$1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function getClient() {
|
|
82
|
+
let c = _client.contents;
|
|
83
|
+
if (c !== undefined) {
|
|
84
|
+
return Primitive_option.valFromOption(c);
|
|
85
|
+
}
|
|
86
|
+
let sdk = await getSdk();
|
|
87
|
+
let c$1 = newOf0(sdk.CognitoIdentityProviderClient);
|
|
88
|
+
_client.contents = Primitive_option.some(c$1);
|
|
89
|
+
return c$1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isPoolGoneError(jsErr) {
|
|
93
|
+
let match = jsErr.name;
|
|
94
|
+
let match$1 = Stdlib_JsExn.message(jsErr);
|
|
95
|
+
if (match == null) {
|
|
96
|
+
if (match$1 !== undefined) {
|
|
97
|
+
return match$1.includes("ResourceNotFoundException");
|
|
98
|
+
} else {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
} else if (match === "ResourceNotFoundException") {
|
|
102
|
+
return true;
|
|
103
|
+
} else if (match$1 !== undefined) {
|
|
104
|
+
return match$1.includes("ResourceNotFoundException");
|
|
105
|
+
} else {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function describePool(userPoolId) {
|
|
111
|
+
let sdk = await getSdk();
|
|
112
|
+
let client = await getClient();
|
|
113
|
+
let result = await client.send(newOf1(sdk.DescribeUserPoolCommand, {
|
|
114
|
+
UserPoolId: userPoolId
|
|
115
|
+
}));
|
|
116
|
+
return Stdlib_Option.flatMap(result.UserPool, Stdlib_JSON.Decode.object);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function applyTrigger(userPoolId, preTokenGenerationArn) {
|
|
120
|
+
let sdk = await getSdk();
|
|
121
|
+
let client = await getClient();
|
|
122
|
+
let described = await describePool(userPoolId);
|
|
123
|
+
if (described === undefined) {
|
|
124
|
+
return Stdlib_JsError.throwWithMessage(`DescribeUserPool returned no pool for "` + userPoolId + `"; refusing to send an UpdateUserPool that would reset it`);
|
|
125
|
+
}
|
|
126
|
+
let input = mergedUpdateInput(described, userPoolId, preTokenGenerationArn);
|
|
127
|
+
return await client.send(newOf1(sdk.UpdateUserPoolCommand, input));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function create(inputs) {
|
|
131
|
+
await applyTrigger(inputs.userPoolId, inputs.preTokenGenerationArn);
|
|
132
|
+
return {
|
|
133
|
+
id: inputs.userPoolId,
|
|
134
|
+
outs: {
|
|
135
|
+
userPoolId: inputs.userPoolId,
|
|
136
|
+
preTokenGenerationArn: inputs.preTokenGenerationArn
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function update(_id, _olds, news) {
|
|
142
|
+
await applyTrigger(news.userPoolId, news.preTokenGenerationArn);
|
|
143
|
+
return {
|
|
144
|
+
outs: {
|
|
145
|
+
userPoolId: news.userPoolId,
|
|
146
|
+
preTokenGenerationArn: news.preTokenGenerationArn
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function delete_(_id, props) {
|
|
152
|
+
try {
|
|
153
|
+
return await applyTrigger(props.userPoolId, undefined);
|
|
154
|
+
} catch (raw_exn) {
|
|
155
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
156
|
+
if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isPoolGoneError)) {
|
|
157
|
+
return log.info("Auth_ActiveRolePoolAttachment", undefined, `user pool ` + props.userPoolId + ` is gone; nothing to detach`);
|
|
158
|
+
}
|
|
159
|
+
throw exn;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function diff_(_id, olds, news) {
|
|
164
|
+
let poolChanged = olds.userPoolId !== news.userPoolId;
|
|
165
|
+
return {
|
|
166
|
+
changes: poolChanged || olds.preTokenGenerationArn !== news.preTokenGenerationArn,
|
|
167
|
+
replaces: poolChanged ? ["userPoolId"] : [],
|
|
168
|
+
deleteBeforeReplace: true
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function read_(id, props) {
|
|
173
|
+
try {
|
|
174
|
+
let described = await describePool(props.userPoolId);
|
|
175
|
+
if (described !== undefined) {
|
|
176
|
+
return {
|
|
177
|
+
id: id,
|
|
178
|
+
props: {
|
|
179
|
+
userPoolId: props.userPoolId,
|
|
180
|
+
preTokenGenerationArn: Stdlib_Option.getOr(attachedTrigger(described), "")
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
} else {
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
} catch (raw_exn) {
|
|
187
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
188
|
+
if (Stdlib_Option.mapOr(Stdlib_JsExn.fromException(exn), false, isPoolGoneError)) {
|
|
189
|
+
return {};
|
|
190
|
+
}
|
|
191
|
+
throw exn;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let provider = {
|
|
196
|
+
create: create,
|
|
197
|
+
update: update,
|
|
198
|
+
delete: delete_,
|
|
199
|
+
diff: diff_,
|
|
200
|
+
read: read_
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
function make(nameOpt, props, opts) {
|
|
204
|
+
let name = nameOpt !== undefined ? nameOpt : "ActiveRolePoolAttachment";
|
|
205
|
+
return new IndexJs.Resource(provider, name, props, opts);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export {
|
|
209
|
+
log,
|
|
210
|
+
readOnlyKeys,
|
|
211
|
+
mergedUpdateInput,
|
|
212
|
+
attachedTrigger,
|
|
213
|
+
newOf1,
|
|
214
|
+
newOf0,
|
|
215
|
+
_sdk,
|
|
216
|
+
_client,
|
|
217
|
+
getSdk,
|
|
218
|
+
getClient,
|
|
219
|
+
isPoolGoneError,
|
|
220
|
+
describePool,
|
|
221
|
+
applyTrigger,
|
|
222
|
+
create,
|
|
223
|
+
update,
|
|
224
|
+
delete_,
|
|
225
|
+
diff_,
|
|
226
|
+
read_,
|
|
227
|
+
provider,
|
|
228
|
+
make,
|
|
229
|
+
}
|
|
230
|
+
/* log Not a pure module */
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// The role a caller chose to act as, as server-side state — and the one door
|
|
2
|
+
// that writes it. See [docs/plans/active-role-narrows-the-token.md] §6.
|
|
3
|
+
//
|
|
4
|
+
// Why state rather than a parameter on the token request: Cognito "doesn't
|
|
5
|
+
// include data from the ClientMetadata parameter in AdminInitiateAuth and
|
|
6
|
+
// InitiateAuth API operations in the request that it passes to the pre token
|
|
7
|
+
// generation function", and a role switch is a `REFRESH_TOKEN_AUTH` flow — an
|
|
8
|
+
// `InitiateAuth` one. The channel that looks purpose-built for carrying the
|
|
9
|
+
// choice is closed on exactly the path that needs it, so the choice is stored
|
|
10
|
+
// here and the trigger reads it on the next refresh.
|
|
11
|
+
//
|
|
12
|
+
// Why not a `custom:activeRole` attribute on the user, which was the first
|
|
13
|
+
// design: a Cognito custom attribute is a one-way door — it can never be removed
|
|
14
|
+
// or retyped once added to a pool. A table avoids permanently amending a
|
|
15
|
+
// customer's user pool to hold a preference.
|
|
16
|
+
//
|
|
17
|
+
// Split in two because the pieces are needed at different points in the deploy
|
|
18
|
+
// graph, and the order is forced rather than chosen:
|
|
19
|
+
//
|
|
20
|
+
// makeTable the rows. Depends on nothing.
|
|
21
|
+
// ↓
|
|
22
|
+
// trigger needs the table to read from ([Auth_ActiveRoleTrigger.res])
|
|
23
|
+
// ↓
|
|
24
|
+
// user pool needs the trigger's ARN as `lambdaConfig.preTokenGeneration`
|
|
25
|
+
// ↓
|
|
26
|
+
// GraphQL API needs the pool as its authorizer
|
|
27
|
+
// ↓
|
|
28
|
+
// makeWriteDoor needs the API to hang a resolver on, and the table to write
|
|
29
|
+
//
|
|
30
|
+
// Provisioning the table with the write door instead would close that chain into
|
|
31
|
+
// a cycle, so the table is hoisted to where the pool is resolved.
|
|
32
|
+
|
|
33
|
+
open PulumiAws
|
|
34
|
+
|
|
35
|
+
type storeTable = {
|
|
36
|
+
name: Pulumi.Output.t<string>,
|
|
37
|
+
arn: Pulumi.Output.t<string>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// JS resolver code (APPSYNC_JS runtime): forward the caller's arguments and the
|
|
41
|
+
// authorizer-verified identity. `sub` is what keys the row, and it comes from
|
|
42
|
+
// here — the mutation has no `sub` argument for a client to supply.
|
|
43
|
+
let invokeCode: Pulumi.Input.t<string> = `import { util } from '@aws-appsync/utils';
|
|
44
|
+
export function request(ctx) {
|
|
45
|
+
const id = ctx.identity;
|
|
46
|
+
return {
|
|
47
|
+
operation: 'Invoke',
|
|
48
|
+
payload: {
|
|
49
|
+
arguments: ctx.args,
|
|
50
|
+
identity: id != null && id.sub != null ? { sub: id.sub } : null
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function response(ctx) {
|
|
55
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
56
|
+
return ctx.result;
|
|
57
|
+
}
|
|
58
|
+
`->Pulumi.Input.make
|
|
59
|
+
|
|
60
|
+
/** One row per subject: `id` is the caller's Cognito `sub`, `activeRole` the
|
|
61
|
+
group they chose, `updatedAt` an operational breadcrumb. No sort key — a caller
|
|
62
|
+
acts as exactly one role at a time, so the row is the whole state. */
|
|
63
|
+
let makeTable = (
|
|
64
|
+
~name: string="ActiveRoleStore",
|
|
65
|
+
~opts: Pulumi.ComponentResource.options,
|
|
66
|
+
): storeTable => {
|
|
67
|
+
let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
|
|
68
|
+
let table = Util_DynamoDb.makeTable(
|
|
69
|
+
~attributes=[{name: "id", type_: "S"}],
|
|
70
|
+
~tags=AWS.Tags.make(
|
|
71
|
+
~name=name ++ "Table",
|
|
72
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
73
|
+
~role=Auth,
|
|
74
|
+
~scope=Platform,
|
|
75
|
+
),
|
|
76
|
+
~opts,
|
|
77
|
+
name,
|
|
78
|
+
)
|
|
79
|
+
{name: table.name, arn: table.arn}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The Lambda behind `Mutation.Platform_SetActiveRole` and its resolver: an
|
|
83
|
+
execution role scoped to Logs + the one table + `AdminListGroupsForUser` on the
|
|
84
|
+
one pool, an AppSync Lambda data source, and the resolver itself. */
|
|
85
|
+
let makeWriteDoor = (
|
|
86
|
+
~api: Pulumi.Output.t<AppSync.GraphQLApi.t>,
|
|
87
|
+
~table: storeTable,
|
|
88
|
+
~cognitoUserPoolId: Pulumi.Input.t<string>,
|
|
89
|
+
~cognitoUserPoolArn: Pulumi.Input.t<string>,
|
|
90
|
+
~name: string="ActiveRoleStore",
|
|
91
|
+
~opts: Pulumi.ComponentResource.options,
|
|
92
|
+
): unit => {
|
|
93
|
+
let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
|
|
94
|
+
|
|
95
|
+
let lambdaRole = IAM.Role.makeWithDefaultPolicy(
|
|
96
|
+
~name=name ++ "Lambda",
|
|
97
|
+
~servicePrincipal=AWS.Lambda.principal->Pulumi.Output.make,
|
|
98
|
+
~tags=AWS.Tags.make(
|
|
99
|
+
~name=name ++ "Lambda",
|
|
100
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
101
|
+
~role=Identity,
|
|
102
|
+
~scope=Platform,
|
|
103
|
+
),
|
|
104
|
+
~opts,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// Logs, the one table, and group membership on the one pool.
|
|
108
|
+
//
|
|
109
|
+
// `AdminListGroupsForUser` is what makes the refusal in the handler real: a
|
|
110
|
+
// narrowed token carries a single group, so membership has to come from
|
|
111
|
+
// Cognito rather than from the token, or a caller who narrowed once could
|
|
112
|
+
// never widen back. Scoped to this pool's ARN, and it is a read — this role
|
|
113
|
+
// cannot add anyone to a group, only observe who is in one.
|
|
114
|
+
let _policy =
|
|
115
|
+
(table.arn, cognitoUserPoolArn->Pulumi.Output.fromInput)
|
|
116
|
+
->Pulumi.Output.all2
|
|
117
|
+
->Pulumi.Output.apply(((tableArn, poolArn)) => {
|
|
118
|
+
let _ = IAM.RolePolicy.make(
|
|
119
|
+
~name=name ++ "LambdaPolicy",
|
|
120
|
+
~args={
|
|
121
|
+
policy: PolicyDocument.make(
|
|
122
|
+
~id=name ++ "LambdaPolicy",
|
|
123
|
+
~statements=[
|
|
124
|
+
{
|
|
125
|
+
sid: "AllowLambdaLogging",
|
|
126
|
+
effect: Allow,
|
|
127
|
+
actions: Actions([
|
|
128
|
+
"logs:CreateLogGroup",
|
|
129
|
+
"logs:CreateLogStream",
|
|
130
|
+
"logs:PutLogEvents",
|
|
131
|
+
]),
|
|
132
|
+
resources: Resource("arn:aws:logs:*:*:*"),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
sid: "AllowActiveRoleRowAccess",
|
|
136
|
+
effect: Allow,
|
|
137
|
+
actions: Actions(["dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:GetItem"]),
|
|
138
|
+
resources: Resource(tableArn),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
sid: "AllowReadingOwnPoolMembership",
|
|
142
|
+
effect: Allow,
|
|
143
|
+
actions: Action("cognito-idp:AdminListGroupsForUser"),
|
|
144
|
+
resources: Resource(poolArn),
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
)
|
|
148
|
+
->PolicyDocument.toJsonString
|
|
149
|
+
->Pulumi.Input.make,
|
|
150
|
+
role: lambdaRole.id->Pulumi.Output.asInput,
|
|
151
|
+
},
|
|
152
|
+
~opts,
|
|
153
|
+
)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
let packageDirs = Dict.fromArray([
|
|
157
|
+
(
|
|
158
|
+
"@reventlessdev/reventless-aws",
|
|
159
|
+
Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws"),
|
|
160
|
+
),
|
|
161
|
+
])
|
|
162
|
+
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
163
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res.mjs",
|
|
164
|
+
~packageDirs,
|
|
165
|
+
~bundleRuntimeExtensions=false,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
let layers =
|
|
169
|
+
Lambda.reventlessLayerArn
|
|
170
|
+
->Option.map(arn => [arn->Pulumi.Input.make])
|
|
171
|
+
->Option.getOr([])
|
|
172
|
+
->Pulumi.Input.make
|
|
173
|
+
|
|
174
|
+
let logGroup = Util_LambdaLogging.makeManagedLogGroup(
|
|
175
|
+
~name=name ++ "Lambda",
|
|
176
|
+
~tags=AWS.Tags.make(
|
|
177
|
+
~name=name ++ "LambdaLogGroup",
|
|
178
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
179
|
+
~role=Logs,
|
|
180
|
+
~scope=Platform,
|
|
181
|
+
),
|
|
182
|
+
~opts,
|
|
183
|
+
(),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
let lambda = Lambda.Function.make(
|
|
187
|
+
~name=name ++ "Lambda",
|
|
188
|
+
~args={
|
|
189
|
+
handler: "index.handler"->Pulumi.Input.make,
|
|
190
|
+
runtime: "nodejs22.x"->Pulumi.Input.make,
|
|
191
|
+
code: code->Pulumi.Input.make,
|
|
192
|
+
sourceCodeHash: sourceCodeHash->Pulumi.Input.make,
|
|
193
|
+
role: lambdaRole.arn->Pulumi.Output.asInput,
|
|
194
|
+
memorySize: 256->Pulumi.Input.make,
|
|
195
|
+
timeout: 30->Pulumi.Input.make,
|
|
196
|
+
layers,
|
|
197
|
+
tags: AWS.Tags.make(
|
|
198
|
+
~name=name ++ "Lambda",
|
|
199
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
200
|
+
~role=Runtime,
|
|
201
|
+
~scope=Platform,
|
|
202
|
+
),
|
|
203
|
+
environment: (
|
|
204
|
+
{
|
|
205
|
+
Lambda.Function.variables: Dict.fromArray([
|
|
206
|
+
("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
|
|
207
|
+
("ACTIVE_ROLE_TABLE", table.name->Pulumi.Output.asInput),
|
|
208
|
+
("COGNITO_USER_POOL_ID", cognitoUserPoolId),
|
|
209
|
+
("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
|
|
210
|
+
("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
|
|
211
|
+
Util_LambdaLogging.logLevelEntry(),
|
|
212
|
+
]),
|
|
213
|
+
}: Lambda.Function.functionEnvironment
|
|
214
|
+
)->Pulumi.Input.make,
|
|
215
|
+
loggingConfig: ?Util_LambdaLogging.loggingConfigFor(logGroup),
|
|
216
|
+
},
|
|
217
|
+
~opts,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
|
|
221
|
+
~name=name ++ "DataSource",
|
|
222
|
+
~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
|
|
223
|
+
~tags=AWS.Tags.make(
|
|
224
|
+
~name=name ++ "DataSource",
|
|
225
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
226
|
+
~role=Identity,
|
|
227
|
+
~scope=Platform,
|
|
228
|
+
),
|
|
229
|
+
~opts,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
let _ =
|
|
233
|
+
(lambda.arn, dataSourceRole.id)
|
|
234
|
+
->Pulumi.Output.all2
|
|
235
|
+
->Pulumi.Output.apply(((lambdaArn, dataSourceRoleId)) => {
|
|
236
|
+
let _attach = IAM.RolePolicy.make(
|
|
237
|
+
~name=name ++ "DataSource",
|
|
238
|
+
~args={
|
|
239
|
+
policy: PolicyDocument.make(
|
|
240
|
+
~id=name ++ "DataSourcePolicy",
|
|
241
|
+
~statements=[
|
|
242
|
+
{
|
|
243
|
+
sid: "AllowDataSourceInvokeLambda",
|
|
244
|
+
effect: Allow,
|
|
245
|
+
actions: Action("lambda:InvokeFunction"),
|
|
246
|
+
resources: Resource(lambdaArn),
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
)
|
|
250
|
+
->PolicyDocument.toJsonString
|
|
251
|
+
->Pulumi.Input.make,
|
|
252
|
+
role: dataSourceRoleId->Pulumi.Input.make,
|
|
253
|
+
},
|
|
254
|
+
~opts,
|
|
255
|
+
)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
let dataSource = AppSync.DataSource.make(
|
|
259
|
+
~name=name ++ "DataSource",
|
|
260
|
+
~args={
|
|
261
|
+
type_: AWS_LAMBDA,
|
|
262
|
+
apiId: api->Pulumi.Output.flatMap(api => api.id)->Pulumi.Output.asInput,
|
|
263
|
+
lambdaConfig: {
|
|
264
|
+
AppSync.DataSource.functionArn: lambda.arn->Pulumi.Output.asInput,
|
|
265
|
+
}->Pulumi.Input.make,
|
|
266
|
+
serviceRoleArn: dataSourceRole.arn->Pulumi.Output.asInput,
|
|
267
|
+
},
|
|
268
|
+
~opts=Some(opts),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
let _resolver = AppSync_Resolver_Native.makeUnitJsResolver(
|
|
272
|
+
~name=name ++ "SetActiveRole",
|
|
273
|
+
~api,
|
|
274
|
+
~dataSourceName=dataSource.name->Pulumi.Output.asInput,
|
|
275
|
+
~type_="Mutation"->Pulumi.Input.make,
|
|
276
|
+
~field="Platform_SetActiveRole"->Pulumi.Input.make,
|
|
277
|
+
~code=invokeCode,
|
|
278
|
+
~opts,
|
|
279
|
+
)
|
|
280
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Aws from "@pulumi/aws";
|
|
4
|
+
import * as IAM$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/IAM.res.mjs";
|
|
5
|
+
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
8
|
+
import * as Lambda$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Lambda/Lambda.res.mjs";
|
|
9
|
+
import * as AWS$ReventlessAws from "../AWS.res.mjs";
|
|
10
|
+
import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
|
|
11
|
+
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
12
|
+
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
13
|
+
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
14
|
+
import * as Util_DynamoDb$ReventlessAws from "../../util/Util_DynamoDb.res.mjs";
|
|
15
|
+
import * as Util_LambdaLogging$ReventlessAws from "../../util/Util_LambdaLogging.res.mjs";
|
|
16
|
+
import * as AppSync_Resolver_Native$ReventlessAws from "../Api/AppSync_Resolver_Native.res.mjs";
|
|
17
|
+
|
|
18
|
+
let invokeCode = `import { util } from '@aws-appsync/utils';
|
|
19
|
+
export function request(ctx) {
|
|
20
|
+
const id = ctx.identity;
|
|
21
|
+
return {
|
|
22
|
+
operation: 'Invoke',
|
|
23
|
+
payload: {
|
|
24
|
+
arguments: ctx.args,
|
|
25
|
+
identity: id != null && id.sub != null ? { sub: id.sub } : null
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function response(ctx) {
|
|
30
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
31
|
+
return ctx.result;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
function makeTable(nameOpt, opts) {
|
|
36
|
+
let name = nameOpt !== undefined ? nameOpt : "ActiveRoleStore";
|
|
37
|
+
let opts$1 = Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts);
|
|
38
|
+
let table = Util_DynamoDb$ReventlessAws.makeTable([{
|
|
39
|
+
name: "id",
|
|
40
|
+
type: "S"
|
|
41
|
+
}], undefined, undefined, undefined, AWS_Tags$ReventlessAws.make(name + "Table", "Platform", "Auth", "Platform", undefined, undefined, undefined, undefined), opts$1, name);
|
|
42
|
+
return {
|
|
43
|
+
name: table.name,
|
|
44
|
+
arn: table.arn
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeWriteDoor(api, table, cognitoUserPoolId, cognitoUserPoolArn, nameOpt, opts) {
|
|
49
|
+
let name = nameOpt !== undefined ? nameOpt : "ActiveRoleStore";
|
|
50
|
+
let opts$1 = Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts);
|
|
51
|
+
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "Lambda", Pulumi.output(AWS$ReventlessAws.Lambda.principal), AWS_Tags$ReventlessAws.make(name + "Lambda", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
52
|
+
Pulumi.all([
|
|
53
|
+
table.arn,
|
|
54
|
+
cognitoUserPoolArn
|
|
55
|
+
]).apply(param => {
|
|
56
|
+
new (Aws.iam.RolePolicy)(name + "LambdaPolicy", {
|
|
57
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "LambdaPolicy", [
|
|
58
|
+
{
|
|
59
|
+
Sid: "AllowLambdaLogging",
|
|
60
|
+
Effect: "Allow",
|
|
61
|
+
Action: [
|
|
62
|
+
"logs:CreateLogGroup",
|
|
63
|
+
"logs:CreateLogStream",
|
|
64
|
+
"logs:PutLogEvents"
|
|
65
|
+
],
|
|
66
|
+
Resource: "arn:aws:logs:*:*:*"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
Sid: "AllowActiveRoleRowAccess",
|
|
70
|
+
Effect: "Allow",
|
|
71
|
+
Action: [
|
|
72
|
+
"dynamodb:PutItem",
|
|
73
|
+
"dynamodb:DeleteItem",
|
|
74
|
+
"dynamodb:GetItem"
|
|
75
|
+
],
|
|
76
|
+
Resource: param[0]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
Sid: "AllowReadingOwnPoolMembership",
|
|
80
|
+
Effect: "Allow",
|
|
81
|
+
Action: "cognito-idp:AdminListGroupsForUser",
|
|
82
|
+
Resource: param[1]
|
|
83
|
+
}
|
|
84
|
+
])),
|
|
85
|
+
role: lambdaRole.id
|
|
86
|
+
}, opts$1);
|
|
87
|
+
});
|
|
88
|
+
let packageDirs = Object.fromEntries([[
|
|
89
|
+
"@reventlessdev/reventless-aws",
|
|
90
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
|
|
91
|
+
]]);
|
|
92
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Auth/Auth_ActiveRoleStore_Ops.res.mjs", packageDirs, undefined, false);
|
|
93
|
+
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
94
|
+
let logGroup = Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", undefined, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
95
|
+
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
96
|
+
handler: "index.handler",
|
|
97
|
+
runtime: "nodejs22.x",
|
|
98
|
+
code: match.code,
|
|
99
|
+
role: lambdaRole.arn,
|
|
100
|
+
memorySize: 256,
|
|
101
|
+
timeout: 30,
|
|
102
|
+
layers: layers,
|
|
103
|
+
tags: AWS_Tags$ReventlessAws.make(name + "Lambda", "Platform", "Runtime", "Platform", undefined, undefined, undefined, undefined),
|
|
104
|
+
environment: {
|
|
105
|
+
variables: Object.fromEntries([
|
|
106
|
+
[
|
|
107
|
+
"Environment",
|
|
108
|
+
Pulumi.getStack()
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
"ACTIVE_ROLE_TABLE",
|
|
112
|
+
table.name
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
"COGNITO_USER_POOL_ID",
|
|
116
|
+
cognitoUserPoolId
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
"NODE_OPTIONS",
|
|
120
|
+
Util_Bundle$ReventlessAws.esmLoaderNodeOptions
|
|
121
|
+
],
|
|
122
|
+
[
|
|
123
|
+
"ESM_FALLBACK_DIRS",
|
|
124
|
+
Util_Bundle$ReventlessAws.esmFallbackDirs
|
|
125
|
+
],
|
|
126
|
+
Util_LambdaLogging$ReventlessAws.logLevelEntry()
|
|
127
|
+
])
|
|
128
|
+
},
|
|
129
|
+
sourceCodeHash: match.sourceCodeHash,
|
|
130
|
+
loggingConfig: Util_LambdaLogging$ReventlessAws.loggingConfigFor(logGroup)
|
|
131
|
+
}, opts$1);
|
|
132
|
+
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
133
|
+
Pulumi.all([
|
|
134
|
+
lambda.arn,
|
|
135
|
+
dataSourceRole.id
|
|
136
|
+
]).apply(param => {
|
|
137
|
+
new (Aws.iam.RolePolicy)(name + "DataSource", {
|
|
138
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "DataSourcePolicy", [{
|
|
139
|
+
Sid: "AllowDataSourceInvokeLambda",
|
|
140
|
+
Effect: "Allow",
|
|
141
|
+
Action: "lambda:InvokeFunction",
|
|
142
|
+
Resource: param[0]
|
|
143
|
+
}])),
|
|
144
|
+
role: param[1]
|
|
145
|
+
}, opts$1);
|
|
146
|
+
});
|
|
147
|
+
let dataSource = new (Aws.appsync.DataSource)(name + "DataSource", {
|
|
148
|
+
type: "AWS_LAMBDA",
|
|
149
|
+
apiId: Output$Pulumi.flatMap(api, api => api.id),
|
|
150
|
+
lambdaConfig: {
|
|
151
|
+
functionArn: lambda.arn
|
|
152
|
+
},
|
|
153
|
+
serviceRoleArn: dataSourceRole.arn
|
|
154
|
+
}, opts$1);
|
|
155
|
+
AppSync_Resolver_Native$ReventlessAws.makeUnitJsResolver(name + "SetActiveRole", api, dataSource.name, "Mutation", "Platform_SetActiveRole", invokeCode, opts$1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
invokeCode,
|
|
160
|
+
makeTable,
|
|
161
|
+
makeWriteDoor,
|
|
162
|
+
}
|
|
163
|
+
/* @pulumi/aws Not a pure module */
|