@kensio/yulin 1.21.24 → 1.21.25
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/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +19 -1
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +21 -3
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.d.ts +6 -3
- package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +3 -1
- package/dist/service/dynamodb/command/scan/sim-dynamodb-scan.js +3 -1
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.d.ts +4 -0
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.js +1 -0
- package/dist/service/dynamodb/table/sim-dynamodb-table-arn.d.ts +13 -0
- package/dist/service/dynamodb/table/sim-dynamodb-table-arn.js +20 -0
- package/docs/services/dynamodb/README.md +26 -0
- package/package.json +2 -2
|
@@ -12,7 +12,8 @@ interface SimDynamoDbAuthorizerProperties {
|
|
|
12
12
|
*
|
|
13
13
|
* AWS maps each DynamoDB API operation to the `dynamodb:` action of the same
|
|
14
14
|
* name, and the resource is the ARN of the table the operation names. ListTables
|
|
15
|
-
* is the exception: it names no table, so it authorizes against `*`.
|
|
15
|
+
* is the exception: it names no table, so it authorizes against `*`. A Query or
|
|
16
|
+
* Scan naming an `IndexName` authorizes against the index's own ARN.
|
|
16
17
|
*/
|
|
17
18
|
export declare class SimDynamoDbAuthorizer {
|
|
18
19
|
private readonly iam;
|
|
@@ -29,6 +30,23 @@ export declare class SimDynamoDbAuthorizer {
|
|
|
29
30
|
* The caller is passed through unchanged so sim IAM can distinguish an
|
|
30
31
|
* omitted caller, which defaults to Account root, from an explicit anonymous
|
|
31
32
|
* caller.
|
|
33
|
+
*
|
|
34
|
+
* A request reaching one of the table's indexes is authorized against the
|
|
35
|
+
* index's ARN. The service authorization reference for DynamoDB gives Query and Scan two
|
|
36
|
+
* resource types, `table` and `index`. The `index` ARN is the table's with
|
|
37
|
+
* `/index/<name>` after it. The developer guide's fine-grained access control
|
|
38
|
+
* page grants index queries on that ARN (its examples 5 and 6). A read naming
|
|
39
|
+
* an index is authorized against the index ARN alone. A statement naming only
|
|
40
|
+
* the table refuses it, and one naming only an index does not reach the
|
|
41
|
+
* table. CDK's `grantReadData` adds `<table ARN>/index/*` only for a table it
|
|
42
|
+
* knows has indexes, and a grant on a table imported by name covers none.
|
|
43
|
+
*
|
|
44
|
+
* The reference also lists `index` for PartiQLSelect, SearchVectors and the
|
|
45
|
+
* contributor insights actions, none of which are simulated. DescribeTable
|
|
46
|
+
* and the item actions take `table` only.
|
|
47
|
+
*
|
|
48
|
+
* The condition keys are the ones the action supplies on the table, with
|
|
49
|
+
* the partition key values read through the index's own key schema.
|
|
32
50
|
*/
|
|
33
51
|
authorizeTable(action: string, tableName: string, caller?: SimAwsCaller, reached?: SimDynamoDbReached): SimAwsResolvedCaller;
|
|
34
52
|
/**
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { SimIamAccessDenied } from "../../../iam/error/sim-iam.error.js";
|
|
2
|
-
import {
|
|
2
|
+
import { simDynamoDbReadArn } from "../../table/sim-dynamodb-table-arn.js";
|
|
3
3
|
import { simDynamoDbConditionContextOf } from "./sim-dynamodb-reached.js";
|
|
4
4
|
/**
|
|
5
5
|
* Applies simulated IAM authorization to DynamoDB requests.
|
|
6
6
|
*
|
|
7
7
|
* AWS maps each DynamoDB API operation to the `dynamodb:` action of the same
|
|
8
8
|
* name, and the resource is the ARN of the table the operation names. ListTables
|
|
9
|
-
* is the exception: it names no table, so it authorizes against `*`.
|
|
9
|
+
* is the exception: it names no table, so it authorizes against `*`. A Query or
|
|
10
|
+
* Scan naming an `IndexName` authorizes against the index's own ARN.
|
|
10
11
|
*/
|
|
11
12
|
export class SimDynamoDbAuthorizer {
|
|
12
13
|
iam;
|
|
@@ -26,9 +27,26 @@ export class SimDynamoDbAuthorizer {
|
|
|
26
27
|
* The caller is passed through unchanged so sim IAM can distinguish an
|
|
27
28
|
* omitted caller, which defaults to Account root, from an explicit anonymous
|
|
28
29
|
* caller.
|
|
30
|
+
*
|
|
31
|
+
* A request reaching one of the table's indexes is authorized against the
|
|
32
|
+
* index's ARN. The service authorization reference for DynamoDB gives Query and Scan two
|
|
33
|
+
* resource types, `table` and `index`. The `index` ARN is the table's with
|
|
34
|
+
* `/index/<name>` after it. The developer guide's fine-grained access control
|
|
35
|
+
* page grants index queries on that ARN (its examples 5 and 6). A read naming
|
|
36
|
+
* an index is authorized against the index ARN alone. A statement naming only
|
|
37
|
+
* the table refuses it, and one naming only an index does not reach the
|
|
38
|
+
* table. CDK's `grantReadData` adds `<table ARN>/index/*` only for a table it
|
|
39
|
+
* knows has indexes, and a grant on a table imported by name covers none.
|
|
40
|
+
*
|
|
41
|
+
* The reference also lists `index` for PartiQLSelect, SearchVectors and the
|
|
42
|
+
* contributor insights actions, none of which are simulated. DescribeTable
|
|
43
|
+
* and the item actions take `table` only.
|
|
44
|
+
*
|
|
45
|
+
* The condition keys are the ones the action supplies on the table, with
|
|
46
|
+
* the partition key values read through the index's own key schema.
|
|
29
47
|
*/
|
|
30
48
|
authorizeTable(action, tableName, caller, reached = {}) {
|
|
31
|
-
return this.authorizeResource(action,
|
|
49
|
+
return this.authorizeResource(action, simDynamoDbReadArn(this.accountRegionScope, tableName, reached.indexName), caller, reached);
|
|
32
50
|
}
|
|
33
51
|
/**
|
|
34
52
|
* Ensure the caller may perform an action on a table's stream.
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What one request reaches in the table it names.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* under one of them leaves it out,
|
|
6
|
-
* that reaches nothing it names.
|
|
4
|
+
* The partition key values and attribute names each become a DynamoDB
|
|
5
|
+
* condition key. A request reaching nothing under one of them leaves it out,
|
|
6
|
+
* as AWS leaves a key out for an operation that reaches nothing it names. The
|
|
7
|
+
* index name decides the resource authorized.
|
|
7
8
|
*/
|
|
8
9
|
export interface SimDynamoDbReached {
|
|
10
|
+
/** The index the request reads, when it names one. */
|
|
11
|
+
readonly indexName?: string | undefined;
|
|
9
12
|
/** The partition key values the request reaches. */
|
|
10
13
|
readonly leadingKeys?: readonly string[] | undefined;
|
|
11
14
|
/** The top-level attribute names the request names. */
|
|
@@ -33,11 +33,13 @@ export class SimDynamoDbQuery {
|
|
|
33
33
|
const expressions = readSimDynamoDbQueryExpressions(input);
|
|
34
34
|
// The one partition key the query reads is what authorization needs, and
|
|
35
35
|
// reading it needs the view rather than the table, so it is read again
|
|
36
|
-
// here against whichever of the two the request names.
|
|
36
|
+
// here against whichever of the two the request names. A query naming an
|
|
37
|
+
// index is authorized against the index's own ARN.
|
|
37
38
|
const table = this.access.required("dynamodb:Query", input.TableName, options?.caller, {
|
|
38
39
|
leadingKeys: (reached) => simDynamoDbLeadingKeyOf(expressions.terms.forTable(reached.view(input.IndexName))
|
|
39
40
|
.partitionKeyValue),
|
|
40
41
|
attributes: expressions.attributes,
|
|
42
|
+
indexName: input.IndexName,
|
|
41
43
|
});
|
|
42
44
|
// What is being read is settled here: the table, or one of its indexes. An
|
|
43
45
|
// IndexName the table does not have is refused rather than read as the
|
|
@@ -37,9 +37,11 @@ export class SimDynamoDbScan {
|
|
|
37
37
|
// input DynamoDB would refuse is refused whether or not the table is
|
|
38
38
|
// there. A scan narrows nothing, so unlike a query its filter may name any
|
|
39
39
|
// attribute, including a key attribute, and needs no key schema to check.
|
|
40
|
+
// A scan naming an index of either kind is authorized against the index's
|
|
41
|
+
// own ARN.
|
|
40
42
|
const segment = readSimDynamoDbScanSegment(input);
|
|
41
43
|
const expressions = readSimDynamoDbScanExpressions(input);
|
|
42
|
-
const table = this.access.required("dynamodb:Scan", input.TableName, options?.caller, { attributes: expressions.attributes });
|
|
44
|
+
const table = this.access.required("dynamodb:Scan", input.TableName, options?.caller, { attributes: expressions.attributes, indexName: input.IndexName });
|
|
43
45
|
// What is being read is settled here: the table, or one of its indexes.
|
|
44
46
|
const view = table.view(input.IndexName);
|
|
45
47
|
const scan = view.scan();
|
|
@@ -8,10 +8,14 @@ import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
|
|
|
8
8
|
* The partition key values need the table to read them, so they arrive as a
|
|
9
9
|
* function of it. The attribute names are read from the request's own
|
|
10
10
|
* parameters before the table is reached, so they arrive as they are.
|
|
11
|
+
*
|
|
12
|
+
* A read naming one of the table's indexes carries the index name. The index
|
|
13
|
+
* is then the resource authorized.
|
|
11
14
|
*/
|
|
12
15
|
export interface SimDynamoDbTableReach {
|
|
13
16
|
readonly leadingKeys?: SimDynamoDbLeadingKeys | undefined;
|
|
14
17
|
readonly attributes?: readonly string[] | undefined;
|
|
18
|
+
readonly indexName?: string | undefined;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* Resolve what a request reaches against the table it named.
|
|
@@ -7,3 +7,16 @@ import type { SimAwsAccountRegionScope } from "../../aws/sim-aws-account-region-
|
|
|
7
7
|
* about to have, as real IAM does before the service handles the request.
|
|
8
8
|
*/
|
|
9
9
|
export declare function simDynamoDbTableArn(accountRegionScope: SimAwsAccountRegionScope, tableName: string): SimArn;
|
|
10
|
+
/**
|
|
11
|
+
* The ARN an index of a table has in an Account and Region.
|
|
12
|
+
*
|
|
13
|
+
* It is the table's ARN with `/index/` and the index name after it, the form
|
|
14
|
+
* the service authorization reference gives the `index` resource type. Like
|
|
15
|
+
* the table, the index need not exist.
|
|
16
|
+
*/
|
|
17
|
+
export declare function simDynamoDbIndexArn(accountRegionScope: SimAwsAccountRegionScope, tableName: string, indexName: string): SimArn;
|
|
18
|
+
/**
|
|
19
|
+
* The ARN a read is authorized against: the table's, or the index's when the
|
|
20
|
+
* read names one.
|
|
21
|
+
*/
|
|
22
|
+
export declare function simDynamoDbReadArn(accountRegionScope: SimAwsAccountRegionScope, tableName: string, indexName: string | undefined): SimArn;
|
|
@@ -7,3 +7,23 @@
|
|
|
7
7
|
export function simDynamoDbTableArn(accountRegionScope, tableName) {
|
|
8
8
|
return `arn:aws:dynamodb:${accountRegionScope.regionName}:${accountRegionScope.accountId}:table/${tableName}`;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* The ARN an index of a table has in an Account and Region.
|
|
12
|
+
*
|
|
13
|
+
* It is the table's ARN with `/index/` and the index name after it, the form
|
|
14
|
+
* the service authorization reference gives the `index` resource type. Like
|
|
15
|
+
* the table, the index need not exist.
|
|
16
|
+
*/
|
|
17
|
+
export function simDynamoDbIndexArn(accountRegionScope, tableName, indexName) {
|
|
18
|
+
return `${simDynamoDbTableArn(accountRegionScope, tableName)}/index/${indexName}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The ARN a read is authorized against: the table's, or the index's when the
|
|
22
|
+
* read names one.
|
|
23
|
+
*/
|
|
24
|
+
export function simDynamoDbReadArn(accountRegionScope, tableName, indexName) {
|
|
25
|
+
if (indexName === undefined) {
|
|
26
|
+
return simDynamoDbTableArn(accountRegionScope, tableName);
|
|
27
|
+
}
|
|
28
|
+
return simDynamoDbIndexArn(accountRegionScope, tableName, indexName);
|
|
29
|
+
}
|
|
@@ -1434,6 +1434,8 @@ from a different partition key is refused, since it names a collection this quer
|
|
|
1434
1434
|
## Reading a global secondary index
|
|
1435
1435
|
|
|
1436
1436
|
Set `IndexName` on `Query` or `Scan` to read an index. Key conditions then use the index key schema.
|
|
1437
|
+
IAM authorizes the read against the index's own ARN, covered under
|
|
1438
|
+
[authorizing a read of an index](#authorizing-a-read-of-an-index).
|
|
1437
1439
|
|
|
1438
1440
|
```typescript sim-dynamodb-query-index
|
|
1439
1441
|
/**
|
|
@@ -3716,6 +3718,26 @@ unauthorized caller cannot find out which names are taken.
|
|
|
3716
3718
|
in the same way, each against the `dynamodb:` action of its own name. `ListTables` names no table.
|
|
3717
3719
|
It authorizes against `*`.
|
|
3718
3720
|
|
|
3721
|
+
### Authorizing a read of an index
|
|
3722
|
+
|
|
3723
|
+
A `Query` or `Scan` of the table authorizes against the table ARN. One naming an `IndexName`
|
|
3724
|
+
authorizes against the index's own ARN, the table ARN followed by `/index/` and the index name:
|
|
3725
|
+
|
|
3726
|
+
```text
|
|
3727
|
+
arn:aws:dynamodb:eu-west-2:111111111111:table/OrdersTable/index/byStatus
|
|
3728
|
+
```
|
|
3729
|
+
|
|
3730
|
+
Global and local secondary indexes work the same way. A statement allowing `dynamodb:Query` on the
|
|
3731
|
+
table ARN alone refuses a query of any index, with an `AccessDenied` naming the index ARN. A
|
|
3732
|
+
statement allowing only an index ARN refuses a query of the table. A caller that reads both needs
|
|
3733
|
+
the table ARN and `<table ARN>/index/*` (or each index ARN) in its policy.
|
|
3734
|
+
|
|
3735
|
+
CDK's `grantReadData` and `grantReadWriteData` add `<table ARN>/index/*` only for a table construct
|
|
3736
|
+
that declares its indexes. A table imported with `Table.fromTableName` declares none, so its grant
|
|
3737
|
+
covers the table ARN alone, and a query of an index fails here as it fails on AWS. Import the table
|
|
3738
|
+
with `Table.fromTableAttributes` and its `globalIndexes` or `localIndexes` to have the grant cover
|
|
3739
|
+
them.
|
|
3740
|
+
|
|
3719
3741
|
### Fine-grained access control with dynamodb:LeadingKeys
|
|
3720
3742
|
|
|
3721
3743
|
`GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`, `DeleteItem` and `BatchWriteItem`
|
|
@@ -3723,6 +3745,8 @@ supply the partition key values they reach as `dynamodb:LeadingKeys`. A policy c
|
|
|
3723
3745
|
key restricts a caller to the items under particular partition keys. AWS scopes one user of a
|
|
3724
3746
|
shared table to their own rows this way.
|
|
3725
3747
|
|
|
3748
|
+
A `Query` of an index supplies the index's partition key value, the value its key condition names.
|
|
3749
|
+
|
|
3726
3750
|
The condition takes the `ForAllValues:` qualifier, as AWS requires for this key. Every partition key
|
|
3727
3751
|
value the request reaches has to match. A batch or a write naming one item outside the allowed set
|
|
3728
3752
|
is refused whole, and nothing it asked for is applied.
|
|
@@ -3847,6 +3871,8 @@ is written.
|
|
|
3847
3871
|
answering with the attributes it projects, and paging with a `LastEvaluatedKey` carrying the index
|
|
3848
3872
|
key and the table key together. A local secondary index also answers a strongly consistent read,
|
|
3849
3873
|
and fetches an unprojected attribute from the base table.
|
|
3874
|
+
- `Query` and `Scan` of an index authorized against the index ARN, for a global and a local
|
|
3875
|
+
secondary index alike.
|
|
3850
3876
|
- `dynamodb:LeadingKeys` on `GetItem`, `BatchGetItem`, `Query`, `PutItem`, `UpdateItem`,
|
|
3851
3877
|
`DeleteItem` and `BatchWriteItem`, carrying the partition key values the request reaches so a
|
|
3852
3878
|
`ForAllValues:` condition can scope a caller to particular items.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kensio/yulin",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.25",
|
|
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/",
|
|
@@ -293,7 +293,7 @@
|
|
|
293
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
|
-
"eslint-plugin-unicorn": "^
|
|
296
|
+
"eslint-plugin-unicorn": "^74.0.0",
|
|
297
297
|
"execa": "^10.0.0",
|
|
298
298
|
"fta-cli": "^3.0.0",
|
|
299
299
|
"hyparquet": "^1.29.2",
|