@kensio/yulin 1.21.12 → 1.21.14

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.
Files changed (89) hide show
  1. package/dist/service/dynamodb/command/authorize/sim-dynamodb-attributes.d.ts +27 -0
  2. package/dist/service/dynamodb/command/authorize/sim-dynamodb-attributes.js +35 -0
  3. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +2 -1
  4. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +10 -4
  5. package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.d.ts +33 -0
  6. package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.js +45 -0
  7. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.d.ts +17 -0
  8. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.js +11 -0
  9. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-get-item.js +5 -0
  10. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.d.ts +6 -0
  11. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.js +16 -0
  12. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.d.ts +2 -0
  13. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.js +14 -22
  14. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.d.ts +10 -2
  15. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.js +1 -1
  16. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write-item.js +11 -1
  17. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.d.ts +6 -0
  18. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.js +8 -8
  19. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.d.ts +5 -1
  20. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.js +9 -2
  21. package/dist/service/dynamodb/command/item/sim-dynamodb-delete-item.js +8 -1
  22. package/dist/service/dynamodb/command/item/sim-dynamodb-get-item.js +10 -2
  23. package/dist/service/dynamodb/command/item/sim-dynamodb-put-item.js +6 -1
  24. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.d.ts +2 -0
  25. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.js +1 -1
  26. package/dist/service/dynamodb/command/item/sim-dynamodb-update-item.js +8 -1
  27. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.d.ts +8 -6
  28. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.js +14 -13
  29. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.d.ts +10 -0
  30. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.js +16 -0
  31. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.d.ts +2 -0
  32. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.js +6 -1
  33. package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +9 -1
  34. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.d.ts +2 -0
  35. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.js +2 -2
  36. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan.js +1 -1
  37. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.d.ts +16 -6
  38. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.js +18 -8
  39. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.d.ts +25 -0
  40. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.js +27 -0
  41. package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-gets.js +1 -1
  42. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.d.ts +9 -1
  43. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.js +2 -2
  44. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-grammar.js +1 -1
  45. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.d.ts +3 -2
  46. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.js +3 -3
  47. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.d.ts +2 -1
  48. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.js +1 -1
  49. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-expression.js +1 -1
  50. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.d.ts +3 -2
  51. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.js +8 -6
  52. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.d.ts +2 -1
  53. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.js +1 -1
  54. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.d.ts +9 -1
  55. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.js +5 -5
  56. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.d.ts +3 -3
  57. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.js +8 -4
  58. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.d.ts +33 -0
  59. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.js +40 -0
  60. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.d.ts +6 -0
  61. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.js +7 -0
  62. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.d.ts +3 -3
  63. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.js +3 -3
  64. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-expression.js +1 -1
  65. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.d.ts +3 -2
  66. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.js +3 -3
  67. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-parser.d.ts +2 -1
  68. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.d.ts +4 -1
  69. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.js +6 -1
  70. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.d.ts +4 -1
  71. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.js +6 -1
  72. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.d.ts +4 -1
  73. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.js +6 -1
  74. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.d.ts +23 -0
  75. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.js +59 -0
  76. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.d.ts +4 -1
  77. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.js +6 -1
  78. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator-parser.js +2 -0
  79. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator.d.ts +9 -2
  80. package/dist/service/iam/authorize/match/condition/sim-iam-policy-condition-matcher.js +1 -1
  81. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.d.ts +8 -1
  82. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.js +10 -1
  83. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.d.ts +4 -1
  84. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.js +6 -1
  85. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.d.ts +4 -1
  86. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.js +6 -1
  87. package/docs/services/dynamodb/README.md +127 -0
  88. package/docs/services/iam/README.md +23 -3
  89. package/package.json +2 -2
@@ -7,7 +7,10 @@ import type { SimIamConditionOperator } from "../sim-iam-condition-operator.js";
7
7
  * scalar or an array, with multiple policy values using OR semantics.
8
8
  */
9
9
  export declare abstract class SimIamScalarStringConditionOperator implements SimIamConditionOperator {
10
- readonly matchesAbsentKey = false;
10
+ /**
11
+ * Answer for a request carrying no value for the key.
12
+ */
13
+ matchesAbsentKey(): boolean;
11
14
  /**
12
15
  * Check whether a given value matches the expected value.
13
16
  */
@@ -6,7 +6,12 @@ import { simIamStringValues } from "./sim-iam-string-values.js";
6
6
  * scalar or an array, with multiple policy values using OR semantics.
7
7
  */
8
8
  export class SimIamScalarStringConditionOperator {
9
- matchesAbsentKey = false;
9
+ /**
10
+ * Answer for a request carrying no value for the key.
11
+ */
12
+ matchesAbsentKey() {
13
+ return false;
14
+ }
10
15
  /**
11
16
  * Check whether a given value matches the expected value.
12
17
  */
@@ -3637,6 +3637,117 @@ unauthorized caller cannot find out which names are taken.
3637
3637
  in the same way, each against the `dynamodb:` action of its own name. `ListTables` names no table.
3638
3638
  It authorizes against `*`.
3639
3639
 
3640
+ ### Fine-grained access control with dynamodb:LeadingKeys
3641
+
3642
+ `GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`, `DeleteItem` and `BatchWriteItem`
3643
+ supply the partition key values they reach as `dynamodb:LeadingKeys`. A policy conditioned on that
3644
+ key restricts a caller to the items under particular partition keys. AWS scopes one user of a
3645
+ shared table to their own rows this way.
3646
+
3647
+ The condition takes the `ForAllValues:` qualifier, as AWS requires for this key. Every partition key
3648
+ value the request reaches has to match. A batch or a write naming one item outside the allowed set
3649
+ is refused whole, and nothing it asked for is applied.
3650
+
3651
+ The same commands, along with `Scan`, supply the top-level attribute names they reach as
3652
+ `dynamodb:Attributes`. A name reaches the condition wherever the request writes one, in a
3653
+ `ProjectionExpression`, an `UpdateExpression`, a `ConditionExpression`, a `FilterExpression`, a
3654
+ `KeyConditionExpression`, and in the `Key` or `Item` a request names outright. A `#` placeholder is
3655
+ resolved first, so the policy is matched against the attribute rather than the alias. A nested path
3656
+ counts as the attribute it starts at, so `address.city` reaches `address`.
3657
+
3658
+ A policy conditioned on `dynamodb:Attributes` has to name every primary key and index key attribute
3659
+ of the table alongside the attributes it is restricting, as AWS requires. A request names its key,
3660
+ and a policy leaving the key attributes out refuses every request that reads or writes an item.
3661
+
3662
+ A request reaching no attribute at all leaves the key out of the condition context, which a
3663
+ `ForAllValues:` condition matches. A `Scan` carrying no expression is the case to watch. AWS asks
3664
+ for a `Null` check beside such a statement to close it, covered under
3665
+ [policy conditions](https://yulinsim.dev/services/iam/#policy-conditions).
3666
+
3667
+ ```typescript sim-dynamodb-leading-keys
3668
+ import { CreateTableCommand, PutItemCommand } from "@aws-sdk/client-dynamodb";
3669
+ import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
3670
+ import { SimAws } from "@kensio/yulin";
3671
+
3672
+ const simAws = new SimAws();
3673
+ const accountId = simAws.defaultAccountId;
3674
+ const region = simAws.defaultRegionName;
3675
+
3676
+ await simAws.dynamoDb().createTable(
3677
+ new CreateTableCommand({
3678
+ TableName: "OrdersTable",
3679
+ KeySchema: [{ AttributeName: "customerId", KeyType: "HASH" }],
3680
+ AttributeDefinitions: [{ AttributeName: "customerId", AttributeType: "S" }],
3681
+ BillingMode: "PAY_PER_REQUEST",
3682
+ }),
3683
+ );
3684
+
3685
+ const roleCreation = await simAws.iam().createRole(
3686
+ new CreateRoleCommand({
3687
+ RoleName: "CustomerRole",
3688
+ AssumeRolePolicyDocument: JSON.stringify({
3689
+ Version: "2012-10-17",
3690
+ Statement: {
3691
+ Effect: "Allow",
3692
+ Principal: { AWS: `arn:aws:iam::${accountId}:root` },
3693
+ Action: "sts:AssumeRole",
3694
+ },
3695
+ }),
3696
+ }),
3697
+ );
3698
+
3699
+ await simAws.iam().putRolePolicy(
3700
+ new PutRolePolicyCommand({
3701
+ RoleName: "CustomerRole",
3702
+ PolicyName: "OwnItemsOnly",
3703
+ PolicyDocument: JSON.stringify({
3704
+ Version: "2012-10-17",
3705
+ Statement: {
3706
+ Effect: "Allow",
3707
+ Action: "dynamodb:PutItem",
3708
+ Resource: `arn:aws:dynamodb:${region}:${accountId}:table/OrdersTable`,
3709
+ Condition: {
3710
+ "ForAllValues:StringEquals": { "dynamodb:LeadingKeys": ["c-1"] },
3711
+ },
3712
+ },
3713
+ }),
3714
+ }),
3715
+ );
3716
+
3717
+ const caller = { kind: "arn", arn: roleCreation.Role.Arn } as const;
3718
+
3719
+ // The customer's own item is written.
3720
+ await simAws.dynamoDb().putItem(
3721
+ new PutItemCommand({
3722
+ TableName: "OrdersTable",
3723
+ Item: { customerId: { S: "c-1" }, total: { N: "25" } },
3724
+ }),
3725
+ { caller },
3726
+ );
3727
+
3728
+ // Another customer's item is refused.
3729
+ try {
3730
+ await simAws.dynamoDb().putItem(
3731
+ new PutItemCommand({
3732
+ TableName: "OrdersTable",
3733
+ Item: { customerId: { S: "c-2" }, total: { N: "25" } },
3734
+ }),
3735
+ { caller },
3736
+ );
3737
+ } catch (error) {
3738
+ console.log(error instanceof Error ? error.name : "unknown error");
3739
+ // "AccessDenied"
3740
+ }
3741
+ ```
3742
+
3743
+ Reading the partition key values needs the table's key schema. The table is found first for that,
3744
+ before the caller is authorized against it. A caller with no permission still hears `AccessDenied`
3745
+ when the table is missing, and an unauthorized caller cannot find out which table names are taken.
3746
+
3747
+ A request carrying no readable partition key value leaves the key out of the condition context. A
3748
+ `ForAllValues:` condition matches a request carrying no value for its key, as it does on AWS. The
3749
+ condition allows such a request.
3750
+
3640
3751
  A transaction is authorized as the operations it is made of rather than as itself. Each action of a
3641
3752
  `TransactWriteItems` needs `dynamodb:PutItem`, `dynamodb:UpdateItem`, `dynamodb:DeleteItem` or
3642
3753
  `dynamodb:ConditionCheckItem` against the table it names, and each `Get` of a `TransactGetItems`
@@ -3657,6 +3768,11 @@ is written.
3657
3768
  answering with the attributes it projects, and paging with a `LastEvaluatedKey` carrying the index
3658
3769
  key and the table key together. A local secondary index also answers a strongly consistent read,
3659
3770
  and fetches an unprojected attribute from the base table.
3771
+ - `dynamodb:LeadingKeys` on `GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`,
3772
+ `DeleteItem` and `BatchWriteItem`, carrying the partition key values the request reaches so a
3773
+ `ForAllValues:` condition can scope a caller to particular items.
3774
+ - `dynamodb:Attributes` on those seven and on `Scan`, carrying the top-level attribute names the
3775
+ request reaches, read from every expression it carries and from its `Key` or `Item`.
3660
3776
  - `DescribeTable`, answering with the full table description, by table name or ARN.
3661
3777
  - `ListTables`, ordered by UTF-8 bytes and paged with `Limit` and `ExclusiveStartTableName`.
3662
3778
  - `DeleteTable`, following the table status DynamoDB moves a deleted table through, and refusing a
@@ -3730,6 +3846,17 @@ Arn`, `Fn::GetAtt … StreamArn` and `Fn::GetAtt … TableId` answering. A CDK `
3730
3846
 
3731
3847
  ## Limitations
3732
3848
 
3849
+ - `dynamodb:LeadingKeys` and `dynamodb:Attributes` are the DynamoDB condition keys supplied.
3850
+ `dynamodb:Select`, `dynamodb:ReturnValues`, `dynamodb:ReturnConsumedCapacity` and the numbered
3851
+ partition key families are absent from the condition context, and a statement conditioned on one
3852
+ of them matches no request. The transactional operations supply neither key, and are authorized by
3853
+ action and table alone.
3854
+ - A partition key value reaches a policy condition as a string for a `S` key and as its digits for
3855
+ an `N` key. A binary partition key has no documented form for the condition key. A request
3856
+ carrying one leaves the value out.
3857
+ - A request against a table that is not there is authorized carrying no leading keys, since there is
3858
+ no key schema to read them with. A `ForAllValues:` condition matches such a request. A caller
3859
+ barred only by that condition learns the table is missing.
3733
3860
  - The document client's PartiQL Commands go unconverted, because PartiQL is an operation this
3734
3861
  simulation lacks yet. `ExecuteStatementCommand`, `BatchExecuteStatementCommand` and
3735
3862
  `ExecuteTransactionCommand` are refused by name, never half converted.
@@ -236,7 +236,7 @@ repository that still says it is there.
236
236
  ## Policy conditions
237
237
 
238
238
  Policy statements can carry `Condition` blocks. Sim IAM currently supports the `StringEquals`,
239
- `StringLike`, `ArnLike`, `ArnEquals` and `NumericLessThanEquals` operators, along with the
239
+ `StringLike`, `ArnLike`, `ArnEquals`, `NumericLessThanEquals` and `Null` operators, along with the
240
240
  `ForAllValues:` and `ForAnyValue:` set variants of `StringEquals` and `StringLike`.
241
241
 
242
242
  The negated operators `StringNotEquals`, `StringNotLike`, `ArnNotEquals` and `ArnNotLike` are
@@ -335,6 +335,23 @@ matches instead, as AWS documents. With no value in the request there is none fo
335
335
  to equal. A `ForAnyValue:` operator answers false for an absent key whatever it wraps, because no
336
336
  request value is there to satisfy it.
337
337
 
338
+ A `ForAllValues:` operator answers true for an absent key, which is also AWS behaviour. Every value
339
+ the request carries matches when it carries none. AWS warns that this leaves a `ForAllValues:`
340
+ `Allow` overly permissive, and asks for a `Null` check with a `false` value beside it. Write the
341
+ guard and sim IAM evaluates it.
342
+
343
+ `Null` asks whether the request carries a value for a context key rather than comparing one. `"true"`
344
+ matches where the key is absent and `"false"` where it is present. A key carrying an empty string or
345
+ an empty list of values counts as absent, which is the null dataset AWS describes. The two values
346
+ can also be written as JSON booleans. Any other policy value leaves the condition matching nothing.
347
+
348
+ ```json
349
+ "Condition": {
350
+ "ForAllValues:StringLike": { "dynamodb:LeadingKeys": ["ORDER#*"] },
351
+ "Null": { "dynamodb:LeadingKeys": "false" }
352
+ }
353
+ ```
354
+
338
355
  ### Statements left unevaluated
339
356
 
340
357
  An operator from outside the list above fails closed. The statement holding it matches nothing, and
@@ -1684,8 +1701,11 @@ Sim IAM models the policy behaviour that multi-service tests most commonly need.
1684
1701
  - Only the condition operators listed above are supported. A statement using any other operator
1685
1702
  fails closed, matching no request. `decision.unevaluatedStatements` names those statements, and a
1686
1703
  test can assert that a decision was reached over policies read in full
1687
- - A positive `ForAllValues:` condition fails to match a request carrying no value for the key, and
1688
- fails to match an empty value set. AWS matches both, and the negated form here matches both
1704
+ - The `...IfExists` operator suffix is absent. AWS reads it as the sibling of `Null`, treating a
1705
+ condition on a key the request leaves out as true. A statement using it fails closed here
1706
+ - A positive `ForAllValues:` condition fails to match an empty value set, where AWS matches one. An
1707
+ absent context key matches, as it does on AWS. A service here leaves a key out where the request
1708
+ carries no value for it, and the empty set is reached only by a caller passing one to `authorize`
1689
1709
  - Signature age is deliberately not enforced. `X-Amz-Date` must be present, well formed, and agree
1690
1710
  with the credential scope date, but is never compared to a clock. A client stamping real time can
1691
1711
  therefore reach a simulation keeping a different one. Session expiry _is_ enforced, against
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.12",
3
+ "version": "1.21.14",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",
@@ -290,7 +290,7 @@
290
290
  "conventional-changelog-conventionalcommits": "9.3.1",
291
291
  "esbuild": "^0.28.2",
292
292
  "eslint": "^10.8.0",
293
- "eslint-plugin-jsdoc": "^63.3.2",
293
+ "eslint-plugin-jsdoc": "^64.0.1",
294
294
  "eslint-plugin-no-secrets": "^2.3.3",
295
295
  "eslint-plugin-security": "^4.0.1",
296
296
  "eslint-plugin-unicorn": "^73.0.0",