@kensio/yulin 1.21.5 → 1.21.7
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/dist/service/cloudformation/resource/cfn/sim-cfn-default-resource-value-adapter.d.ts +8 -1
- package/dist/service/cloudformation/resource/cfn/sim-cfn-default-resource-value-adapter.js +9 -2
- package/dist/service/cloudformation/resource/cfn/sim-cfn-resource-value-adapter.d.ts +6 -1
- package/dist/service/cloudformation/resource/cfn/sim-cfn-resource-value-adapter.js +2 -1
- package/dist/service/cloudformation/resource/create/sim-cfn-resource-create-operation.d.ts +4 -0
- package/dist/service/cloudformation/resource/create/sim-cfn-resource-create-operation.js +6 -0
- package/dist/service/cloudformation/resource/sim-cfn-resource-record.d.ts +19 -0
- package/dist/service/cloudformation/resource/sim-cfn-resource-record.js +26 -0
- package/dist/service/cloudformation/resource/sim-cfn-resource.js +1 -0
- package/dist/service/cloudformation/resource/unsupported/sim-cfn-named-skip.d.ts +26 -0
- package/dist/service/cloudformation/resource/unsupported/sim-cfn-named-skip.js +31 -0
- package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-creator.d.ts +5 -2
- package/dist/service/cloudfront/cfn/cache-policy/sim-cfn-cf-cache-policy-creator.js +10 -2
- package/dist/service/cloudfront/cfn/origin-access-control/sim-cfn-cf-oac-creator.d.ts +5 -2
- package/dist/service/cloudfront/cfn/origin-access-control/sim-cfn-cf-oac-creator.js +10 -2
- package/dist/service/cloudfront/cfn/origin-request-policy/sim-cfn-cf-orp-creator.d.ts +5 -2
- package/dist/service/cloudfront/cfn/origin-request-policy/sim-cfn-cf-orp-creator.js +10 -2
- package/dist/service/cloudfront/cfn/policy/sim-cfn-cf-policy-creators.d.ts +33 -0
- package/dist/service/cloudfront/cfn/policy/sim-cfn-cf-policy-creators.js +39 -0
- package/dist/service/cloudfront/cfn/response-headers-policy/sim-cfn-cf-rh-policy-creator.d.ts +5 -2
- package/dist/service/cloudfront/cfn/response-headers-policy/sim-cfn-cf-rh-policy-creator.js +10 -2
- package/dist/service/cloudfront/cfn/sim-cfn-cf-authorizer.d.ts +40 -0
- package/dist/service/cloudfront/cfn/sim-cfn-cf-authorizer.js +50 -0
- package/dist/service/cloudfront/cfn/sim-cfn-cloudfront-resource-factory.d.ts +1 -4
- package/dist/service/cloudfront/cfn/sim-cfn-cloudfront-resource-factory.js +14 -46
- package/dist/service/cloudfront/sim-cloudfront-commands.d.ts +6 -0
- package/dist/service/cloudfront/sim-cloudfront-commands.js +7 -0
- package/dist/service/cloudfront/sim-cloudfront.d.ts +6 -0
- package/dist/service/cloudfront/sim-cloudfront.js +7 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-command-paths.d.ts +8 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-command-paths.js +62 -0
- package/dist/service/dynamodb/document/sim-dynamodb-document-routes.d.ts +1 -1
- package/dist/service/dynamodb/document/sim-dynamodb-document-routes.js +3 -1
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +1 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +4 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +26 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +3 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.d.ts +0 -7
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.js +0 -20
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.d.ts +13 -0
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.js +59 -0
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.d.ts +1 -1
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.js +1 -1
- package/dist/service/iam/authorize/sim-iam-action-authorizer.d.ts +8 -3
- package/dist/service/iam/authorize/sim-iam-action-authorizer.js +13 -4
- package/dist/service/iam/cfn/role/sim-cfn-iam-role-creator.js +1 -0
- package/dist/service/iam/cfn/role/sim-cfn-iam-role-properties-parser.d.ts +1 -0
- package/dist/service/iam/cfn/role/sim-cfn-iam-role-properties-parser.js +21 -14
- package/dist/service/iam/cfn/{sim-cfn-iam-optional-string.d.ts → sim-cfn-iam-property.d.ts} +10 -2
- package/dist/service/iam/cfn/sim-cfn-iam-property.js +31 -0
- package/dist/service/iam/cfn/user/sim-cfn-iam-user-properties-parser.js +1 -1
- package/dist/service/iam/command/role/create-role/create-role-record-factory.js +3 -0
- package/dist/service/iam/command/role/create-role/create-role.command.d.ts +10 -0
- package/dist/service/iam/command/role/get-role/get-role.command.d.ts +2 -0
- package/dist/service/iam/command/role/get-role/get-role.handler.js +2 -0
- package/dist/service/iam/command/role/sim-iam-role-command-handlers.js +15 -1
- package/dist/service/iam/role/sim-iam-role-boundary.d.ts +24 -0
- package/dist/service/iam/role/sim-iam-role-boundary.js +24 -0
- package/dist/service/iam/role/sim-iam-role-with-policy.factory.d.ts +1 -0
- package/dist/service/iam/role/sim-iam-role.d.ts +10 -0
- package/dist/service/iam/serve/sim-iam-query-entity.d.ts +6 -1
- package/dist/service/iam/serve/sim-iam-query-entity.js +10 -4
- package/dist/service/iam/serve/sim-iam-query-role-operations.js +15 -3
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +1 -1
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +3 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +25 -3
- package/dist/service/lambda/cfn/function/sim-cfn-lambda-runtime-skip.d.ts +6 -0
- package/dist/service/lambda/cfn/function/sim-cfn-lambda-runtime-skip.js +9 -2
- package/dist/service/route53/command/create-hosted-zone/sim-route53-zone-id.js +23 -1
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-creator.js +1 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.d.ts +9 -4
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.js +18 -6
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.d.ts +10 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.js +16 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-creator.js +1 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +11 -6
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +18 -9
- package/docs/README.md +33 -4
- package/docs/ai-skill/README.md +52 -54
- package/docs/cli/README.md +84 -94
- package/docs/factories/README.md +42 -54
- package/docs/lint/README.md +41 -67
- package/docs/non-aws-dependencies/README.md +72 -168
- package/docs/sdk/README.md +97 -98
- package/docs/serve/README.md +192 -898
- package/docs/services/cloudformation/README.md +88 -23
- package/docs/services/cloudfront/README.md +109 -0
- package/docs/services/dynamodb/README.md +128 -8
- package/docs/services/eventbridge/README.md +10 -2
- package/docs/services/iam/README.md +125 -2
- package/docs/services/lambda/README.md +7 -1
- package/docs/services/route53/README.md +11 -0
- package/docs/services/sns/README.md +13 -7
- package/docs/services/ssm/README.md +6 -4
- package/docs/terraform/README.md +108 -126
- package/docs/time/README.md +80 -120
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/service/iam/cfn/sim-cfn-iam-optional-string.js +0 -17
|
@@ -1055,8 +1055,15 @@ simulated CloudFront hostname, such as `e123example.cloudfront.net`.
|
|
|
1055
1055
|
#### Values from a skipped Resource
|
|
1056
1056
|
|
|
1057
1057
|
A Resource that was skipped, because its type is outside the simulation or because there is no
|
|
1058
|
-
simulated Resource to create at all, still answers both intrinsics.
|
|
1059
|
-
|
|
1058
|
+
simulated Resource to create at all, still answers both intrinsics.
|
|
1059
|
+
|
|
1060
|
+
What `Ref` answers with follows how far the refusal got. A service that had already worked out the
|
|
1061
|
+
Resource's name answers with that name. That is the name real CloudFormation would have produced,
|
|
1062
|
+
and the simulation holds it whether or not the Resource behind it was created. Sim Lambda declining
|
|
1063
|
+
a function on its `Runtime` is the case to know about, and it covers CDK's own Python providers. A
|
|
1064
|
+
Resource refused before any name existed answers with the logical ID.
|
|
1065
|
+
|
|
1066
|
+
`Fn::GetAtt` answers with the string `<logical ID>.<attribute name>` for either of them.
|
|
1060
1067
|
|
|
1061
1068
|
```typescript sim-cloudformation-skipped-resource-values
|
|
1062
1069
|
/**
|
|
@@ -1071,28 +1078,31 @@ const stack = await simAws.cloudFormation().deployTemplate({
|
|
|
1071
1078
|
stackName: "stand-in-stack",
|
|
1072
1079
|
template: {
|
|
1073
1080
|
Resources: {
|
|
1074
|
-
|
|
1075
|
-
Type: "AWS::
|
|
1081
|
+
SearchApi: {
|
|
1082
|
+
Type: "AWS::AppSync::GraphQLApi",
|
|
1083
|
+
Properties: {
|
|
1084
|
+
Name: "search",
|
|
1085
|
+
},
|
|
1076
1086
|
},
|
|
1077
1087
|
},
|
|
1078
1088
|
Outputs: {
|
|
1079
|
-
|
|
1080
|
-
|
|
1089
|
+
SearchApiRef: { Value: { Ref: "SearchApi" } },
|
|
1090
|
+
SearchApiArn: { Value: { "Fn::GetAtt": ["SearchApi", "Arn"] } },
|
|
1081
1091
|
},
|
|
1082
1092
|
},
|
|
1083
1093
|
});
|
|
1084
1094
|
|
|
1085
1095
|
await stack.waitForDeployComplete();
|
|
1086
1096
|
|
|
1087
|
-
console.log(stack.output("
|
|
1088
|
-
// "
|
|
1097
|
+
console.log(stack.output("SearchApiRef"));
|
|
1098
|
+
// "SearchApi"
|
|
1089
1099
|
|
|
1090
|
-
console.log(stack.output("
|
|
1091
|
-
// "
|
|
1100
|
+
console.log(stack.output("SearchApiArn"));
|
|
1101
|
+
// "SearchApi.Arn"
|
|
1092
1102
|
|
|
1093
1103
|
for (const skipped of stack.skippedResources) {
|
|
1094
1104
|
console.log(skipped.logicalId, skipped.skippedReason);
|
|
1095
|
-
// "
|
|
1105
|
+
// "SearchApi Unsupported sim CloudFormation Resource service AppSync"
|
|
1096
1106
|
}
|
|
1097
1107
|
```
|
|
1098
1108
|
|
|
@@ -1101,7 +1111,7 @@ every Resource holding a `Ref` or `Fn::GetAtt` to a skipped Resource would fail
|
|
|
1101
1111
|
every Resource depending on those, until one EventBridge rule took the whole stack down with it. The skip
|
|
1102
1112
|
stays where it happened.
|
|
1103
1113
|
|
|
1104
|
-
A
|
|
1114
|
+
A logical ID is deliberately shaped unlike an ARN. It fails closed wherever the simulator reads it.
|
|
1105
1115
|
|
|
1106
1116
|
- In an IAM policy `Resource` it matches no ARN, so a caller relying on that statement is denied.
|
|
1107
1117
|
- In a property that is parsed as an ARN it is refused as malformed, and that Resource fails.
|
|
@@ -1112,9 +1122,58 @@ A stand-in is deliberately shaped unlike an ARN. It fails closed wherever the si
|
|
|
1112
1122
|
SDK call fails the way a call for a missing resource does. A `PutItem` naming the skipped table
|
|
1113
1123
|
gets `ResourceNotFoundException: No DynamoDB Table named Orders`.
|
|
1114
1124
|
|
|
1115
|
-
A
|
|
1116
|
-
|
|
1117
|
-
|
|
1125
|
+
A generated name behaves differently, and is meant to. It is the name an account would have held, so
|
|
1126
|
+
anything reading it reads a well-formed name. CDK writes a function's log group name as
|
|
1127
|
+
`/aws/lambda/` joined to a `Ref` of the function, and the log group is created under
|
|
1128
|
+
`/aws/lambda/<stack>-<logical ID>-<suffix>` even where the function was declined. A deploy Role
|
|
1129
|
+
scoped to `<stack>-*` and `/aws/lambda/<stack>-*` covers the deployment, the way it does on AWS.
|
|
1130
|
+
|
|
1131
|
+
```typescript sim-cloudformation-skipped-resource-generated-name
|
|
1132
|
+
/**
|
|
1133
|
+
* A function skipped on its Runtime, and the Log Group named after it.
|
|
1134
|
+
*/
|
|
1135
|
+
|
|
1136
|
+
import { SimAws } from "@kensio/yulin";
|
|
1137
|
+
|
|
1138
|
+
const simAws = new SimAws();
|
|
1139
|
+
|
|
1140
|
+
const stack = await simAws.cloudFormation().deployTemplate({
|
|
1141
|
+
stackName: "ReportStack",
|
|
1142
|
+
template: {
|
|
1143
|
+
Resources: {
|
|
1144
|
+
ReportFunction: {
|
|
1145
|
+
Type: "AWS::Lambda::Function",
|
|
1146
|
+
Properties: {
|
|
1147
|
+
Role: "arn:aws:iam::111111111111:role/ReportRole",
|
|
1148
|
+
Handler: "index.handler",
|
|
1149
|
+
Runtime: "python3.13",
|
|
1150
|
+
Code: { ZipFile: "def handler(event, context): return 'report'" },
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
ReportFunctionLogGroup: {
|
|
1154
|
+
Type: "AWS::Logs::LogGroup",
|
|
1155
|
+
Properties: {
|
|
1156
|
+
LogGroupName: {
|
|
1157
|
+
"Fn::Join": ["", ["/aws/lambda/", { Ref: "ReportFunction" }]],
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
},
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
await stack.waitForDeployComplete();
|
|
1166
|
+
|
|
1167
|
+
console.log(stack.getResource("ReportFunction")?.refValue);
|
|
1168
|
+
// "ReportStack-ReportFunction-42d643ca8338"
|
|
1169
|
+
|
|
1170
|
+
console.log(simAws.logs().allLogGroups()[0]?.logGroupName);
|
|
1171
|
+
// "/aws/lambda/ReportStack-ReportFunction-42d643ca8338"
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
The Resource is absent all the same, and the name is no evidence that it is there. Invoking the
|
|
1175
|
+
function above fails with `ResourceNotFoundException`. `stack.skippedResources` is where to find out
|
|
1176
|
+
which Resources were left out and why, under
|
|
1118
1177
|
[Inspecting stacks and resources](#inspecting-stacks-and-resources).
|
|
1119
1178
|
|
|
1120
1179
|
### `Fn::Join`
|
|
@@ -3976,8 +4035,11 @@ const stack = await simAws.cloudFormation().deployTemplate({
|
|
|
3976
4035
|
BucketName: "skipped-site-bucket",
|
|
3977
4036
|
},
|
|
3978
4037
|
},
|
|
3979
|
-
|
|
3980
|
-
Type: "AWS::
|
|
4038
|
+
SearchApi: {
|
|
4039
|
+
Type: "AWS::AppSync::GraphQLApi",
|
|
4040
|
+
Properties: {
|
|
4041
|
+
Name: "search",
|
|
4042
|
+
},
|
|
3981
4043
|
},
|
|
3982
4044
|
},
|
|
3983
4045
|
},
|
|
@@ -3986,10 +4048,10 @@ const stack = await simAws.cloudFormation().deployTemplate({
|
|
|
3986
4048
|
await stack.waitForDeployComplete();
|
|
3987
4049
|
|
|
3988
4050
|
console.log(stack.skippedResources.map((resource) => resource.logicalId));
|
|
3989
|
-
// ["
|
|
4051
|
+
// ["SearchApi"]
|
|
3990
4052
|
|
|
3991
|
-
console.log(stack.getResource("
|
|
3992
|
-
// "Unsupported sim CloudFormation Resource service
|
|
4053
|
+
console.log(stack.getResource("SearchApi")?.skippedReason);
|
|
4054
|
+
// "Unsupported sim CloudFormation Resource service AppSync"
|
|
3993
4055
|
```
|
|
3994
4056
|
|
|
3995
4057
|
A skipped Resource is still there for `stack.getResource(...)`, and still answers `Ref` and `Fn::GetAtt` with
|
|
@@ -4033,8 +4095,11 @@ const stack = await simAws.cloudFormation().deployTemplate({
|
|
|
4033
4095
|
Description: "/opt/awscli/aws",
|
|
4034
4096
|
},
|
|
4035
4097
|
},
|
|
4036
|
-
|
|
4037
|
-
Type: "AWS::
|
|
4098
|
+
SearchApi: {
|
|
4099
|
+
Type: "AWS::AppSync::GraphQLApi",
|
|
4100
|
+
Properties: {
|
|
4101
|
+
Name: "search",
|
|
4102
|
+
},
|
|
4038
4103
|
},
|
|
4039
4104
|
},
|
|
4040
4105
|
},
|
|
@@ -4043,7 +4108,7 @@ const stack = await simAws.cloudFormation().deployTemplate({
|
|
|
4043
4108
|
await stack.waitForDeployComplete();
|
|
4044
4109
|
|
|
4045
4110
|
console.log(stack.skippedResources.map((resource) => resource.logicalId));
|
|
4046
|
-
// ["
|
|
4111
|
+
// ["SearchApi"]
|
|
4047
4112
|
|
|
4048
4113
|
console.log(stack.inertResources.map((resource) => resource.logicalId));
|
|
4049
4114
|
// ["AwsCliLayer"]
|
|
@@ -259,6 +259,11 @@ with a 200 for a URL the Bucket has no object for. It is one of the same error c
|
|
|
259
259
|
CloudFront allows. Where the response page is itself missing, the viewer gets the status from
|
|
260
260
|
fetching it, as in CloudFront.
|
|
261
261
|
|
|
262
|
+
Behind an origin access control, a response page the Bucket does not hold answers 403 where a public
|
|
263
|
+
Bucket answers 404. The Bucket policy an origin access control is written with grants `s3:GetObject`
|
|
264
|
+
and no `s3:ListBucket`, and S3 refuses a key whose absence it may not report. A test whose Bucket
|
|
265
|
+
was never loaded with the site's error page sees that refusal in place of the page.
|
|
266
|
+
|
|
262
267
|
A viewer-response function never sees a custom error page. CloudFront runs no viewer-response
|
|
263
268
|
function once the Origin has answered 400 or higher, and simulated CloudFront does the same, for a
|
|
264
269
|
CloudFront Function and a Lambda@Edge function alike. The status the Origin returned is what decides
|
|
@@ -3781,6 +3786,110 @@ A CDK stack needs no hand-editing.
|
|
|
3781
3786
|
other store. Handing back an empty store would let a Function that lost its association run to
|
|
3782
3787
|
completion and quietly take every default.
|
|
3783
3788
|
|
|
3789
|
+
## Permissions
|
|
3790
|
+
|
|
3791
|
+
Every CloudFront command goes through simulated IAM. So does every CloudFront Resource a
|
|
3792
|
+
CloudFormation Stack creates, decided as the principal the deployment runs as. Stand a project's own
|
|
3793
|
+
execution policy up as a deploy Role and the Stack finds out what the policy leaves out. A
|
|
3794
|
+
deployment naming no principal is decided as the Account root.
|
|
3795
|
+
|
|
3796
|
+
The action is the `cloudfront:` name of the operation. A Distribution action is decided against
|
|
3797
|
+
`arn:aws:cloudfront::<account>:distribution/<id>`, a Function action against `function/<name>` and a
|
|
3798
|
+
key value store action against `key-value-store/<id>`. A create action has nothing to name until it
|
|
3799
|
+
succeeds and is decided against `*`. A policy granting one has to write the wildcard.
|
|
3800
|
+
|
|
3801
|
+
These are the actions the simulation asks about:
|
|
3802
|
+
|
|
3803
|
+
- `CreateDistribution`, `GetDistribution`, `UpdateDistribution` and `DeleteDistribution`
|
|
3804
|
+
- `CreateFunction`, `ListFunctions`, `DescribeFunction`, `GetFunction` and `DeleteFunction`
|
|
3805
|
+
- `CreateInvalidation`, `GetInvalidation` and `ListInvalidations`
|
|
3806
|
+
- `CreateKeyValueStore`, `ListKeyValueStores`, `DescribeKeyValueStore`, `UpdateKeyValueStore` and
|
|
3807
|
+
`DeleteKeyValueStore`, alongside the data API's own `cloudfront-keyvaluestore:` actions on the
|
|
3808
|
+
keys inside a store
|
|
3809
|
+
- `CreateCachePolicy` and `DeleteCachePolicy`
|
|
3810
|
+
- `CreateOriginRequestPolicy` and `DeleteOriginRequestPolicy`
|
|
3811
|
+
- `CreateResponseHeadersPolicy` and `DeleteResponseHeadersPolicy`
|
|
3812
|
+
- `CreateOriginAccessControl` and `DeleteOriginAccessControl`
|
|
3813
|
+
|
|
3814
|
+
The last four pairs reach IAM through CloudFormation alone, since a template is the only way to make
|
|
3815
|
+
one of those four here. Each delete is decided against the ARN of the thing it names, such as
|
|
3816
|
+
`arn:aws:cloudfront::<account>:cache-policy/<id>`.
|
|
3817
|
+
|
|
3818
|
+
```typescript sim-cloudfront-deploy-permissions
|
|
3819
|
+
/**
|
|
3820
|
+
* A deploy Role refused the cache policy its Stack declares.
|
|
3821
|
+
*/
|
|
3822
|
+
|
|
3823
|
+
import { SimAws } from "@kensio/yulin";
|
|
3824
|
+
|
|
3825
|
+
const simAws = new SimAws();
|
|
3826
|
+
|
|
3827
|
+
// A deploy Role allowed CloudFormation and S3, and no CloudFront action.
|
|
3828
|
+
const { Role } = await simAws.iam().createRole({
|
|
3829
|
+
input: {
|
|
3830
|
+
RoleName: "DeployRole",
|
|
3831
|
+
AssumeRolePolicyDocument: JSON.stringify({
|
|
3832
|
+
Version: "2012-10-17",
|
|
3833
|
+
Statement: [
|
|
3834
|
+
{
|
|
3835
|
+
Effect: "Allow",
|
|
3836
|
+
Principal: { Service: "cloudformation.amazonaws.com" },
|
|
3837
|
+
Action: "sts:AssumeRole",
|
|
3838
|
+
},
|
|
3839
|
+
],
|
|
3840
|
+
}),
|
|
3841
|
+
},
|
|
3842
|
+
});
|
|
3843
|
+
|
|
3844
|
+
await simAws.iam().putRolePolicy({
|
|
3845
|
+
input: {
|
|
3846
|
+
RoleName: "DeployRole",
|
|
3847
|
+
PolicyName: "DeployPolicy",
|
|
3848
|
+
PolicyDocument: JSON.stringify({
|
|
3849
|
+
Version: "2012-10-17",
|
|
3850
|
+
Statement: [
|
|
3851
|
+
{
|
|
3852
|
+
Effect: "Allow",
|
|
3853
|
+
Action: ["cloudformation:*", "s3:*"],
|
|
3854
|
+
Resource: "*",
|
|
3855
|
+
},
|
|
3856
|
+
],
|
|
3857
|
+
}),
|
|
3858
|
+
},
|
|
3859
|
+
});
|
|
3860
|
+
|
|
3861
|
+
try {
|
|
3862
|
+
await simAws.cloudFormation().deployTemplate({
|
|
3863
|
+
stackName: "site-stack",
|
|
3864
|
+
caller: { kind: "arn", arn: Role.Arn },
|
|
3865
|
+
template: {
|
|
3866
|
+
Resources: {
|
|
3867
|
+
SiteCachePolicy: {
|
|
3868
|
+
Type: "AWS::CloudFront::CachePolicy",
|
|
3869
|
+
Properties: {
|
|
3870
|
+
CachePolicyConfig: {
|
|
3871
|
+
Name: "site-caching",
|
|
3872
|
+
MinTTL: 0,
|
|
3873
|
+
ParametersInCacheKeyAndForwardedToOrigin: {
|
|
3874
|
+
EnableAcceptEncodingGzip: false,
|
|
3875
|
+
CookiesConfig: { CookieBehavior: "none" },
|
|
3876
|
+
HeadersConfig: { HeaderBehavior: "none" },
|
|
3877
|
+
QueryStringsConfig: { QueryStringBehavior: "none" },
|
|
3878
|
+
},
|
|
3879
|
+
},
|
|
3880
|
+
},
|
|
3881
|
+
},
|
|
3882
|
+
},
|
|
3883
|
+
},
|
|
3884
|
+
});
|
|
3885
|
+
} catch (error) {
|
|
3886
|
+
// Sim CloudFormation Resource SiteCachePolicy creation failed: User:
|
|
3887
|
+
// arn:aws:iam::...:role/DeployRole is not authorized to perform:
|
|
3888
|
+
// cloudfront:CreateCachePolicy on resource: *
|
|
3889
|
+
console.log((error as Error).message);
|
|
3890
|
+
}
|
|
3891
|
+
```
|
|
3892
|
+
|
|
3784
3893
|
## Available functionality
|
|
3785
3894
|
|
|
3786
3895
|
Sim CloudFront currently supports:
|
|
@@ -2443,6 +2443,123 @@ console.log(output.Responses[1]); // {}
|
|
|
2443
2443
|
That is the difference from `BatchGetItem`, which leaves a missing item out of its answer
|
|
2444
2444
|
altogether. Here the answers stay lined up with the Gets that asked for them.
|
|
2445
2445
|
|
|
2446
|
+
### Transactions through the document client
|
|
2447
|
+
|
|
2448
|
+
`TransactWriteCommand` and `TransactGetCommand` from `@aws-sdk/lib-dynamodb` reach the same two
|
|
2449
|
+
operations. Every action's `Item`, `Key` and `ExpressionAttributeValues` are converted on the way in,
|
|
2450
|
+
and the `Item` of each `Responses` entry on the way out.
|
|
2451
|
+
|
|
2452
|
+
```typescript sim-dynamodb-document-transactions
|
|
2453
|
+
/**
|
|
2454
|
+
* Writing an order and the claim on its code together, in plain JavaScript.
|
|
2455
|
+
*/
|
|
2456
|
+
|
|
2457
|
+
import { CreateTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
2458
|
+
import {
|
|
2459
|
+
DynamoDBDocumentClient,
|
|
2460
|
+
TransactGetCommand,
|
|
2461
|
+
TransactWriteCommand,
|
|
2462
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
2463
|
+
|
|
2464
|
+
import { SimSdk } from "@kensio/yulin/sdk";
|
|
2465
|
+
|
|
2466
|
+
using simSdk = new SimSdk();
|
|
2467
|
+
|
|
2468
|
+
const documents = DynamoDBDocumentClient.from(
|
|
2469
|
+
new DynamoDBClient({ region: "eu-west-2" }),
|
|
2470
|
+
);
|
|
2471
|
+
simSdk.intercept(documents);
|
|
2472
|
+
|
|
2473
|
+
for (const [tableName, keyName] of [
|
|
2474
|
+
["OrdersTable", "orderId"],
|
|
2475
|
+
["ClaimsTable", "code"],
|
|
2476
|
+
] as const) {
|
|
2477
|
+
await documents.send(
|
|
2478
|
+
new CreateTableCommand({
|
|
2479
|
+
TableName: tableName,
|
|
2480
|
+
KeySchema: [{ AttributeName: keyName, KeyType: "HASH" }],
|
|
2481
|
+
AttributeDefinitions: [{ AttributeName: keyName, AttributeType: "S" }],
|
|
2482
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
2483
|
+
}),
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
await simSdk.simAws.backgroundTasksComplete();
|
|
2487
|
+
|
|
2488
|
+
const claim = {
|
|
2489
|
+
TableName: "ClaimsTable",
|
|
2490
|
+
ConditionExpression: "attribute_not_exists(code)",
|
|
2491
|
+
};
|
|
2492
|
+
|
|
2493
|
+
// The order and the claim on its code go in together.
|
|
2494
|
+
await documents.send(
|
|
2495
|
+
new TransactWriteCommand({
|
|
2496
|
+
TransactItems: [
|
|
2497
|
+
{
|
|
2498
|
+
Put: {
|
|
2499
|
+
TableName: "OrdersTable",
|
|
2500
|
+
Item: { orderId: "order-1", total: 42, lines: [{ sku: "widget" }] },
|
|
2501
|
+
},
|
|
2502
|
+
},
|
|
2503
|
+
{ Put: { ...claim, Item: { code: "ABC123", orderId: "order-1" } } },
|
|
2504
|
+
],
|
|
2505
|
+
}),
|
|
2506
|
+
);
|
|
2507
|
+
|
|
2508
|
+
const read = await documents.send(
|
|
2509
|
+
new TransactGetCommand({
|
|
2510
|
+
TransactItems: [
|
|
2511
|
+
{ Get: { TableName: "OrdersTable", Key: { orderId: "order-1" } } },
|
|
2512
|
+
{ Get: { TableName: "ClaimsTable", Key: { code: "ABC123" } } },
|
|
2513
|
+
],
|
|
2514
|
+
}),
|
|
2515
|
+
);
|
|
2516
|
+
|
|
2517
|
+
const lines = read.Responses?.[0]?.Item?.["lines"] as { sku: string }[];
|
|
2518
|
+
console.log(lines[0]?.sku); // widget
|
|
2519
|
+
console.log(read.Responses?.[1]?.Item?.["orderId"]); // order-1
|
|
2520
|
+
|
|
2521
|
+
// A second order wanting the same code loses the claim, and loses the order
|
|
2522
|
+
// with it.
|
|
2523
|
+
try {
|
|
2524
|
+
await documents.send(
|
|
2525
|
+
new TransactWriteCommand({
|
|
2526
|
+
TransactItems: [
|
|
2527
|
+
{
|
|
2528
|
+
Put: {
|
|
2529
|
+
TableName: "OrdersTable",
|
|
2530
|
+
Item: { orderId: "order-2", total: 7 },
|
|
2531
|
+
},
|
|
2532
|
+
},
|
|
2533
|
+
{ Put: { ...claim, Item: { code: "ABC123", orderId: "order-2" } } },
|
|
2534
|
+
],
|
|
2535
|
+
}),
|
|
2536
|
+
);
|
|
2537
|
+
} catch (error) {
|
|
2538
|
+
const cancelled = error as {
|
|
2539
|
+
name: string;
|
|
2540
|
+
CancellationReasons?: { Code: string }[];
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
console.log(cancelled.name); // "TransactionCanceledException"
|
|
2544
|
+
console.log(cancelled.CancellationReasons?.map((reason) => reason.Code));
|
|
2545
|
+
// ["None", "ConditionalCheckFailed"]
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
const second = await documents.send(
|
|
2549
|
+
new TransactGetCommand({
|
|
2550
|
+
TransactItems: [
|
|
2551
|
+
{ Get: { TableName: "OrdersTable", Key: { orderId: "order-2" } } },
|
|
2552
|
+
],
|
|
2553
|
+
}),
|
|
2554
|
+
);
|
|
2555
|
+
|
|
2556
|
+
console.log(second.Responses?.[0]); // {}
|
|
2557
|
+
```
|
|
2558
|
+
|
|
2559
|
+
`CancellationReasons` arrive as the low-level Command reports them. There is one per action, in
|
|
2560
|
+
`TransactItems` order. A reason's `Item` holds AttributeValues, because a cancelled transaction is
|
|
2561
|
+
thrown, and the real document client converts nothing on the way out of a transactional write.
|
|
2562
|
+
|
|
2446
2563
|
## Expiring items with time to live
|
|
2447
2564
|
|
|
2448
2565
|
`UpdateTimeToLive` names the attribute a table expires items by, and `DescribeTimeToLive` reports
|
|
@@ -2930,8 +3047,11 @@ console.log(tags.has("priority")); // true
|
|
|
2930
3047
|
```
|
|
2931
3048
|
|
|
2932
3049
|
`PutCommand`, `GetCommand`, `DeleteCommand`, `UpdateCommand`, `QueryCommand`, `ScanCommand`,
|
|
2933
|
-
`BatchWriteCommand` and `
|
|
2934
|
-
|
|
3050
|
+
`BatchWriteCommand`, `BatchGetCommand`, `TransactWriteCommand` and `TransactGetCommand` are
|
|
3051
|
+
converted. The two transactional ones have
|
|
3052
|
+
[a section of their own](#transactions-through-the-document-client). A document Command with no route
|
|
3053
|
+
here, such as the PartiQL `ExecuteStatementCommand`, is refused by name before anything tries to
|
|
3054
|
+
convert its values.
|
|
2935
3055
|
|
|
2936
3056
|
Intercept the document client itself. `DynamoDBDocumentClient.from(client)` builds a separate object
|
|
2937
3057
|
outside the `DynamoDBClient` class, so intercepting the base client leaves Commands sent through the
|
|
@@ -3621,15 +3741,15 @@ Arn`, `Fn::GetAtt … StreamArn` and `Fn::GetAtt … TableId` answering. A CDK `
|
|
|
3621
3741
|
- SDK interception, and an intercepted `DynamoDBClient` or `DynamoDBStreamsClient` reaches the
|
|
3622
3742
|
simulation.
|
|
3623
3743
|
- The `@aws-sdk/lib-dynamodb` document client, with `PutCommand`, `GetCommand`, `DeleteCommand`,
|
|
3624
|
-
`UpdateCommand`, `QueryCommand`, `ScanCommand`, `BatchWriteCommand
|
|
3625
|
-
converting native JavaScript values on the way in
|
|
3626
|
-
paging through a simulated table.
|
|
3744
|
+
`UpdateCommand`, `QueryCommand`, `ScanCommand`, `BatchWriteCommand`, `BatchGetCommand`,
|
|
3745
|
+
`TransactWriteCommand` and `TransactGetCommand` converting native JavaScript values on the way in
|
|
3746
|
+
and out, and `paginateQuery` and `paginateScan` paging through a simulated table.
|
|
3627
3747
|
|
|
3628
3748
|
## Limitations
|
|
3629
3749
|
|
|
3630
|
-
- The document client's
|
|
3631
|
-
|
|
3632
|
-
|
|
3750
|
+
- The document client's PartiQL Commands go unconverted, because PartiQL is an operation this
|
|
3751
|
+
simulation lacks yet. `ExecuteStatementCommand`, `BatchExecuteStatementCommand` and
|
|
3752
|
+
`ExecuteTransactionCommand` are refused by name, never half converted.
|
|
3633
3753
|
- A document client's translate config goes unread. The marshalling options it was built with do not
|
|
3634
3754
|
apply. See [the SDK docs](https://yulinsim.dev/sdk/#limitations).
|
|
3635
3755
|
- `Expected`, `ConditionalOperator`, `AttributeUpdates`, `KeyConditions`, `QueryFilter` and
|
|
@@ -919,6 +919,12 @@ at deploy time naming the property and the Resource. The alternative is a rule t
|
|
|
919
919
|
event where one field was asked for. Tearing the stack down removes the buses, rules and targets it
|
|
920
920
|
created.
|
|
921
921
|
|
|
922
|
+
`Tags` is the one difference from the commands, which refuse it outright. A template's tags are
|
|
923
|
+
usually the whole stack's (a CDK app calling `Tags.of(app).add(...)` tags every rule and bus in it),
|
|
924
|
+
and a rule routes the same events whether it carries them or not. They are recorded as an ignored
|
|
925
|
+
property and the deploy stands. Nothing reads them back. A rule or a bus deployed with tags behaves
|
|
926
|
+
as though the template had never named them.
|
|
927
|
+
|
|
922
928
|
## Reading back a failed delivery
|
|
923
929
|
|
|
924
930
|
Real EventBridge tells the caller nothing about a failed delivery. A `PutEvents` that matched a rule
|
|
@@ -1101,7 +1107,8 @@ no permission for.
|
|
|
1101
1107
|
- EventBridge Scheduler is a separate service, simulated separately. See
|
|
1102
1108
|
[simulated Scheduler](https://yulinsim.dev/services/scheduler/).
|
|
1103
1109
|
- Rule tags, a rule `RoleArn`, managed rules and the `ENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS`
|
|
1104
|
-
state are all refused
|
|
1110
|
+
state are all refused at `PutRule`. An `AWS::Events::Rule` carrying `Tags` deploys with the tags
|
|
1111
|
+
dropped and the property recorded.
|
|
1105
1112
|
- Deleting an event bus deletes its rules, and deleting a rule deletes its targets. Real EventBridge
|
|
1106
1113
|
refuses to delete either while it still has what hangs off it.
|
|
1107
1114
|
- `AWS::Events::EventBusPolicy`, `AWS::Events::Archive`, `AWS::Events::Connection` and
|
|
@@ -1113,6 +1120,7 @@ no permission for.
|
|
|
1113
1120
|
stricter than real AWS.
|
|
1114
1121
|
- Putting an event onto another account's or region's bus is refused.
|
|
1115
1122
|
- Partner event buses and partner event sources are refused, as are event bus tags, encryption with
|
|
1116
|
-
a customer managed key, dead letter queues, logging configuration and global endpoints.
|
|
1123
|
+
a customer managed key, dead letter queues, logging configuration and global endpoints. An
|
|
1124
|
+
`AWS::Events::EventBus` carrying `Tags` deploys with the tags dropped and the property recorded.
|
|
1117
1125
|
- Archives, replay, schema registry and discovery, API destinations and connections are not
|
|
1118
1126
|
simulated.
|
|
@@ -260,6 +260,11 @@ from the resolved caller and `aws:RequestedRegion` from the Region the request w
|
|
|
260
260
|
service supplies under either name is overwritten by the derived one. Context-key names are matched
|
|
261
261
|
case-insensitively, while string values remain case-sensitive.
|
|
262
262
|
|
|
263
|
+
IAM's own requests carry two service keys. A Role handed to a service carries
|
|
264
|
+
[`iam:PassedToService`](#passing-a-role-to-a-service), and a `CreateRole` request carries the
|
|
265
|
+
boundary it declares as
|
|
266
|
+
[`iam:PermissionsBoundary`](#creating-a-role-under-a-permissions-boundary).
|
|
267
|
+
|
|
263
268
|
Every simulated service supplies its own Region. A policy conditioned on `aws:RequestedRegion`
|
|
264
269
|
therefore sees the Region of the service that handled the request, whichever Region the caller was
|
|
265
270
|
in, and a CloudFormation deployment carries the Region of the Stack it is deploying. IAM, CloudFront
|
|
@@ -1045,6 +1050,115 @@ await simAws.backgroundTasksComplete();
|
|
|
1045
1050
|
A request naming no caller is decided as the Account root, which may pass any Role. A test that never
|
|
1046
1051
|
mentions IAM keeps working.
|
|
1047
1052
|
|
|
1053
|
+
## Creating a Role under a permissions boundary
|
|
1054
|
+
|
|
1055
|
+
An account that requires every Role to carry a permissions boundary allows `iam:CreateRole` only
|
|
1056
|
+
under `StringEquals` on `iam:PermissionsBoundary`. Its CloudFormation execution policy is written
|
|
1057
|
+
that way, and the CDK `PermissionsBoundary.fromName(...)` aspect puts the boundary ARN on every Role
|
|
1058
|
+
the app synthesizes. A `CreateRoleCommand` carrying `PermissionsBoundary` supplies the key, and
|
|
1059
|
+
`AWS::IAM::Role` supplies it from the template property of the same name.
|
|
1060
|
+
|
|
1061
|
+
A request that leaves the property out leaves the key unset. A positive operator over an unset key
|
|
1062
|
+
matches nothing, and the guard refuses the Role. A request naming some other policy is refused the
|
|
1063
|
+
same way. A test can assert both refusals, and that is what makes the policy worth standing up as a
|
|
1064
|
+
deploy Role.
|
|
1065
|
+
|
|
1066
|
+
`CreateRole` and `GetRole` then describe the boundary the Role carries, in the attachment shape IAM
|
|
1067
|
+
answers with (`PermissionsBoundaryType` and `PermissionsBoundaryArn`). `ListRoles` leaves the field
|
|
1068
|
+
out, as IAM does, and points a caller at `GetRole`. The boundary is not yet evaluated as a policy
|
|
1069
|
+
source of its own, and does not narrow what the Role may then do
|
|
1070
|
+
(see [Limitations](#limitations)).
|
|
1071
|
+
|
|
1072
|
+
```typescript sim-iam-permissions-boundary
|
|
1073
|
+
/**
|
|
1074
|
+
* Deploying into an account that requires a permissions boundary.
|
|
1075
|
+
*/
|
|
1076
|
+
|
|
1077
|
+
import { GetRoleCommand } from "@aws-sdk/client-iam";
|
|
1078
|
+
import { SimAws } from "@kensio/yulin";
|
|
1079
|
+
|
|
1080
|
+
const simAws = new SimAws({ defaultAccountId: "123456789012" });
|
|
1081
|
+
const boundaryArn = "arn:aws:iam::123456789012:policy/DeveloperBoundary";
|
|
1082
|
+
|
|
1083
|
+
const deployer = await simAws.iam().makeDeployRole({
|
|
1084
|
+
roleName: "cfn-exec",
|
|
1085
|
+
policyDocument: {
|
|
1086
|
+
Version: "2012-10-17",
|
|
1087
|
+
Statement: [
|
|
1088
|
+
{
|
|
1089
|
+
Effect: "Allow",
|
|
1090
|
+
Action: "iam:CreateRole",
|
|
1091
|
+
Resource: "*",
|
|
1092
|
+
Condition: {
|
|
1093
|
+
StringEquals: { "iam:PermissionsBoundary": boundaryArn },
|
|
1094
|
+
},
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
Effect: "Allow",
|
|
1098
|
+
Action: ["cloudformation:*", "iam:PutRolePolicy", "iam:PassRole"],
|
|
1099
|
+
Resource: "*",
|
|
1100
|
+
},
|
|
1101
|
+
],
|
|
1102
|
+
},
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
const jobRoleTrust = {
|
|
1106
|
+
Version: "2012-10-17",
|
|
1107
|
+
Statement: {
|
|
1108
|
+
Effect: "Allow",
|
|
1109
|
+
Action: "sts:AssumeRole",
|
|
1110
|
+
Principal: { Service: "lambda.amazonaws.com" },
|
|
1111
|
+
},
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
const stack = await simAws.cloudFormation().deployTemplate({
|
|
1115
|
+
stackName: "guarded-stack",
|
|
1116
|
+
template: {
|
|
1117
|
+
Resources: {
|
|
1118
|
+
JobRole: {
|
|
1119
|
+
Type: "AWS::IAM::Role",
|
|
1120
|
+
Properties: {
|
|
1121
|
+
RoleName: "JobRole",
|
|
1122
|
+
PermissionsBoundary: boundaryArn,
|
|
1123
|
+
AssumeRolePolicyDocument: jobRoleTrust,
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
caller: deployer,
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
// CREATE_COMPLETE
|
|
1132
|
+
console.log(stack.getResource("JobRole")?.status);
|
|
1133
|
+
|
|
1134
|
+
const roleRead = await simAws
|
|
1135
|
+
.iam()
|
|
1136
|
+
.getRole(new GetRoleCommand({ RoleName: "JobRole" }));
|
|
1137
|
+
|
|
1138
|
+
// arn:aws:iam::123456789012:policy/DeveloperBoundary
|
|
1139
|
+
console.log(roleRead.Role.PermissionsBoundary?.PermissionsBoundaryArn);
|
|
1140
|
+
|
|
1141
|
+
try {
|
|
1142
|
+
await simAws.cloudFormation().deployTemplate({
|
|
1143
|
+
stackName: "unguarded-stack",
|
|
1144
|
+
template: {
|
|
1145
|
+
Resources: {
|
|
1146
|
+
BareRole: {
|
|
1147
|
+
Type: "AWS::IAM::Role",
|
|
1148
|
+
Properties: {
|
|
1149
|
+
RoleName: "BareRole",
|
|
1150
|
+
AssumeRolePolicyDocument: jobRoleTrust,
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
caller: deployer,
|
|
1156
|
+
});
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
console.error("A Role declaring no boundary was refused", error);
|
|
1159
|
+
}
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1048
1162
|
## A Role for a CloudFormation deployment
|
|
1049
1163
|
|
|
1050
1164
|
`makeDeployRole(...)` creates a Role from a policy document and hands back a caller. Pass it as
|
|
@@ -1241,6 +1355,11 @@ the logical ID when it is omitted, and inline `Policies` declared on a Role are
|
|
|
1241
1355
|
Role's inline policies. The `ReadOnlyAccess` policy above is attached to `LambdaExecutionRole`.
|
|
1242
1356
|
Authorization for that Role reads the policy document the template gave it.
|
|
1243
1357
|
|
|
1358
|
+
A Role's `PermissionsBoundary` reaches the authorization decision the deployment is made under, and
|
|
1359
|
+
is recorded on the Role it names.
|
|
1360
|
+
[Creating a Role under a permissions boundary](#creating-a-role-under-a-permissions-boundary) covers
|
|
1361
|
+
the guard it exists for.
|
|
1362
|
+
|
|
1244
1363
|
### Users
|
|
1245
1364
|
|
|
1246
1365
|
An `AWS::IAM::User` creates a User in the Stack's Account. `UserName` names it and falls back to the
|
|
@@ -1534,6 +1653,8 @@ Sim IAM currently supports:
|
|
|
1534
1653
|
- IAM authorization at simulated service boundaries, such as Route53 actions
|
|
1535
1654
|
- `iam:PassRole` authorization of a Role handed to simulated Lambda, Scheduler, EventBridge, Step
|
|
1536
1655
|
Functions, ECS or Firehose, with `iam:PassedToService` supplied
|
|
1656
|
+
- `iam:CreateRole` authorization against `iam:PermissionsBoundary`, from the `PermissionsBoundary`
|
|
1657
|
+
a request or an `AWS::IAM::Role` declares
|
|
1537
1658
|
- Resolving the caller of an HTTP request, from an `x-sim-aws-caller` header or a verified SigV4
|
|
1538
1659
|
signature, defaulting to anonymous, and the resource it is made on behalf of from
|
|
1539
1660
|
`x-sim-aws-source-arn` and `x-sim-aws-source-account`
|
|
@@ -1551,8 +1672,10 @@ Sim IAM models the policy behaviour that multi-service tests most commonly need.
|
|
|
1551
1672
|
|
|
1552
1673
|
- Groups are absent. An `AWS::IAM::User` naming one fails, and an `AWS::IAM::Policy` naming one
|
|
1553
1674
|
fails too
|
|
1554
|
-
- Permissions boundaries and session policies are not evaluated.
|
|
1555
|
-
|
|
1675
|
+
- Permissions boundaries and session policies are not evaluated. A boundary a `CreateRole` request
|
|
1676
|
+
declares is authorized against `iam:PermissionsBoundary` and described by `GetRole`, and stops
|
|
1677
|
+
there. Service control policies are evaluated, and are attached through
|
|
1678
|
+
[simulated Organizations](https://yulinsim.dev/services/organizations/ "Simulated Organizations service control policies usage docs")
|
|
1556
1679
|
- Managed Policies have a single version, and the policy version commands are absent
|
|
1557
1680
|
- A policy document is measured against IAM's character limit one document at a time. The
|
|
1558
1681
|
20,480-character cap on the sum of a User's inline policies is absent, and so is the cap on how
|
|
@@ -1702,6 +1702,10 @@ Reporting individual failures can be tested against a made event too. The handle
|
|
|
1702
1702
|
a template naming one is refused. The same Resource deploys a
|
|
1703
1703
|
[stream mapping](#stream-mappings-in-templates), which has to have one.
|
|
1704
1704
|
|
|
1705
|
+
`Tags` is the one property recorded rather than refused. A template's tags are usually the whole
|
|
1706
|
+
stack's (a CDK app calling `Tags.of(app).add(...)` tags every mapping in it), and a mapping delivers
|
|
1707
|
+
the same records whether it carries them or not. The deploy stands and nothing reads them back.
|
|
1708
|
+
|
|
1705
1709
|
## Triggering a function from a DynamoDB stream
|
|
1706
1710
|
|
|
1707
1711
|
An event source mapping also connects a [simulated table's stream](https://yulinsim.dev/services/dynamodb/#capturing-changes-with-a-stream "Simulated DynamoDB streams docs")
|
|
@@ -4268,7 +4272,9 @@ Current documented limitations:
|
|
|
4268
4272
|
- SQS queues, DynamoDB streams and Kinesis streams are the only event sources. Kafka, DocumentDB and
|
|
4269
4273
|
Kinesis enhanced fan-out consumers are refused outright, and so are `FilterCriteria`,
|
|
4270
4274
|
`ScalingConfig`, `DestinationConfig`, `BisectBatchOnFunctionError`, `ParallelizationFactor`,
|
|
4271
|
-
`TumblingWindowInSeconds` and the other mapping inputs this simulation has no behaviour for.
|
|
4275
|
+
`TumblingWindowInSeconds` and the other mapping inputs this simulation has no behaviour for. An
|
|
4276
|
+
`AWS::Lambda::EventSourceMapping` carrying `Tags` is the exception, and deploys with the tags
|
|
4277
|
+
dropped and the property recorded.
|
|
4272
4278
|
- A failed stream batch waits 1, 2, 4, 8 and 16 seconds between attempts, where AWS documents no
|
|
4273
4279
|
delay. That is deliberate. A delay of zero falls due at the instant the clock already reads, and a
|
|
4274
4280
|
handler that always throws would leave `advanceBy` with work falling due forever. A mapping that
|