@kensio/yulin 1.21.11 → 1.21.13
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/cloudfront/controller/web-acl/sim-cf-web-acl-guard.js +1 -0
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +1 -1
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +23 -4
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.d.ts +33 -0
- package/dist/service/dynamodb/command/authorize/sim-dynamodb-leading-keys.js +45 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-get-item.js +2 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.d.ts +10 -2
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.js +1 -1
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write-item.js +7 -1
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.d.ts +6 -0
- package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write.js +8 -8
- package/dist/service/dynamodb/command/item/sim-dynamodb-delete-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-get-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-put-item.js +2 -1
- package/dist/service/dynamodb/command/item/sim-dynamodb-update-item.js +2 -1
- package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +6 -1
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.d.ts +16 -6
- package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.js +34 -8
- package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator.d.ts +4 -1
- package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.d.ts +5 -1
- package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.js +5 -1
- package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
- package/dist/service/wafv2/cfn/web-acl/sim-cfn-waf-web-acl-config.js +1 -0
- package/dist/service/wafv2/command/web-acl/sim-waf-web-acl-output.js +1 -0
- package/dist/service/wafv2/command/web-acl/sim-wafv2-unsimulated-web-acl-input.js +0 -6
- package/dist/service/wafv2/command/web-acl/sim-wafv2-web-acl-commands.js +1 -0
- package/dist/service/wafv2/command/web-acl/web-acl.command.d.ts +3 -1
- package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +8 -0
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +14 -1
- package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +7 -1
- package/dist/service/wafv2/index.d.ts +2 -1
- package/dist/service/wafv2/index.js +2 -1
- package/dist/service/wafv2/managed/sim-waf-managed-request.js +1 -2
- package/dist/service/wafv2/sim-wafv2.js +2 -1
- package/dist/service/wafv2/statement/sim-waf-field-content.d.ts +9 -6
- package/dist/service/wafv2/statement/sim-waf-field-content.js +16 -10
- package/dist/service/wafv2/statement/sim-waf-set-field.js +3 -3
- package/dist/service/wafv2/web-acl/sim-waf-association-config.d.ts +54 -0
- package/dist/service/wafv2/web-acl/sim-waf-association-config.js +92 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl-configuration.d.ts +2 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl.d.ts +9 -0
- package/dist/service/wafv2/web-acl/sim-waf-web-acl.js +15 -0
- package/docs/services/dynamodb/README.md +108 -0
- package/docs/services/iam/README.md +8 -2
- package/docs/services/wafv2/README.md +19 -5
- package/package.json +2 -2
|
@@ -29,7 +29,7 @@ export declare class SimDynamoDbAuthorizer {
|
|
|
29
29
|
* omitted caller, which defaults to Account root, from an explicit anonymous
|
|
30
30
|
* caller.
|
|
31
31
|
*/
|
|
32
|
-
authorizeTable(action: string, tableName: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
|
|
32
|
+
authorizeTable(action: string, tableName: string, caller?: SimAwsCaller, leadingKeys?: readonly string[]): SimAwsResolvedCaller;
|
|
33
33
|
/**
|
|
34
34
|
* Ensure the caller may perform an action on a table's stream.
|
|
35
35
|
*
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { SimIamAccessDenied } from "../../../iam/error/sim-iam.error.js";
|
|
2
2
|
import { simDynamoDbTableArn } from "../../table/sim-dynamodb-table-arn.js";
|
|
3
|
+
import { simDynamoDbLeadingKeysConditionKey } from "./sim-dynamodb-leading-keys.js";
|
|
4
|
+
/**
|
|
5
|
+
* The condition values a request carries for the table it names.
|
|
6
|
+
*
|
|
7
|
+
* A request reaching no partition key value supplies none rather than an empty
|
|
8
|
+
* list, leaving the key absent from the context as it is on AWS for an
|
|
9
|
+
* operation that names no item.
|
|
10
|
+
*/
|
|
11
|
+
function conditionContextOf(leadingKeys) {
|
|
12
|
+
if (leadingKeys === undefined || leadingKeys.length === 0) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
return { [simDynamoDbLeadingKeysConditionKey]: leadingKeys };
|
|
16
|
+
}
|
|
3
17
|
/**
|
|
4
18
|
* Applies simulated IAM authorization to DynamoDB requests.
|
|
5
19
|
*
|
|
@@ -26,8 +40,8 @@ export class SimDynamoDbAuthorizer {
|
|
|
26
40
|
* omitted caller, which defaults to Account root, from an explicit anonymous
|
|
27
41
|
* caller.
|
|
28
42
|
*/
|
|
29
|
-
authorizeTable(action, tableName, caller) {
|
|
30
|
-
return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller);
|
|
43
|
+
authorizeTable(action, tableName, caller, leadingKeys) {
|
|
44
|
+
return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller, leadingKeys);
|
|
31
45
|
}
|
|
32
46
|
/**
|
|
33
47
|
* Ensure the caller may perform an action on a table's stream.
|
|
@@ -51,8 +65,13 @@ export class SimDynamoDbAuthorizer {
|
|
|
51
65
|
authorizeAnyTable(action, caller) {
|
|
52
66
|
return this.authorizeResource(action, "*", caller);
|
|
53
67
|
}
|
|
54
|
-
authorizeResource(action, resource, caller) {
|
|
55
|
-
const decision = this.iam.authorize({
|
|
68
|
+
authorizeResource(action, resource, caller, leadingKeys) {
|
|
69
|
+
const decision = this.iam.authorize({
|
|
70
|
+
action,
|
|
71
|
+
resource,
|
|
72
|
+
caller,
|
|
73
|
+
conditionContext: conditionContextOf(leadingKeys),
|
|
74
|
+
});
|
|
56
75
|
if (decision.isDenied) {
|
|
57
76
|
throw new SimIamAccessDenied({
|
|
58
77
|
principal: decision.caller.principal,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
|
|
2
|
+
import type { SimDynamoDbValue } from "../../item/sim-dynamodb-value.js";
|
|
3
|
+
import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
|
|
4
|
+
/**
|
|
5
|
+
* The condition key DynamoDB names a request's partition key values by.
|
|
6
|
+
*/
|
|
7
|
+
export declare const simDynamoDbLeadingKeysConditionKey = "dynamodb:LeadingKeys";
|
|
8
|
+
/**
|
|
9
|
+
* The partition key values one request reaches in one table.
|
|
10
|
+
*
|
|
11
|
+
* Which attribute holds the partition key is the table's to say, so a command
|
|
12
|
+
* hands over how to read the values rather than the values themselves. The
|
|
13
|
+
* table is found before the caller is authorized against it, and the values
|
|
14
|
+
* are read from it in between.
|
|
15
|
+
*/
|
|
16
|
+
export type SimDynamoDbLeadingKeys = (table: SimDynamoDbTable) => readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Read the partition key values a run of items or keys carries.
|
|
19
|
+
*
|
|
20
|
+
* The items are read when authorization asks for them rather than at the call
|
|
21
|
+
* site, which is what keeps a request DynamoDB would refuse being refused
|
|
22
|
+
* after the caller has been authorized rather than before.
|
|
23
|
+
*
|
|
24
|
+
* An item with no readable partition key value is left out.
|
|
25
|
+
*/
|
|
26
|
+
export declare function simDynamoDbLeadingKeysOf(items: () => readonly SimDynamoDbItem[]): SimDynamoDbLeadingKeys;
|
|
27
|
+
/**
|
|
28
|
+
* Read the one partition key value a key condition names.
|
|
29
|
+
*
|
|
30
|
+
* The value is already held against the key schema of what is being read, so
|
|
31
|
+
* the table says nothing more about it here.
|
|
32
|
+
*/
|
|
33
|
+
export declare function simDynamoDbLeadingKeyOf(value: SimDynamoDbValue): readonly string[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The condition key DynamoDB names a request's partition key values by.
|
|
3
|
+
*/
|
|
4
|
+
export const simDynamoDbLeadingKeysConditionKey = "dynamodb:LeadingKeys";
|
|
5
|
+
/**
|
|
6
|
+
* Write one partition key value the way an IAM policy condition holds it.
|
|
7
|
+
*
|
|
8
|
+
* A policy condition value is a string. A string key is its text and a number
|
|
9
|
+
* key is its digits. Binary has no form documented for the condition key, and
|
|
10
|
+
* a request carrying a binary partition key leaves the value out rather than
|
|
11
|
+
* inventing an encoding for it.
|
|
12
|
+
*/
|
|
13
|
+
function leadingKeyText(value) {
|
|
14
|
+
if (value?.kind === "S") {
|
|
15
|
+
return value.text;
|
|
16
|
+
}
|
|
17
|
+
if (value?.kind === "N") {
|
|
18
|
+
return value.number.text;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the partition key values a run of items or keys carries.
|
|
24
|
+
*
|
|
25
|
+
* The items are read when authorization asks for them rather than at the call
|
|
26
|
+
* site, which is what keeps a request DynamoDB would refuse being refused
|
|
27
|
+
* after the caller has been authorized rather than before.
|
|
28
|
+
*
|
|
29
|
+
* An item with no readable partition key value is left out.
|
|
30
|
+
*/
|
|
31
|
+
export function simDynamoDbLeadingKeysOf(items) {
|
|
32
|
+
return (table) => items()
|
|
33
|
+
.map((item) => leadingKeyText(item.attribute(table.keySchema.hashKeyAttributeName)))
|
|
34
|
+
.filter((text) => text !== undefined);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read the one partition key value a key condition names.
|
|
38
|
+
*
|
|
39
|
+
* The value is already held against the key schema of what is being read, so
|
|
40
|
+
* the table says nothing more about it here.
|
|
41
|
+
*/
|
|
42
|
+
export function simDynamoDbLeadingKeyOf(value) {
|
|
43
|
+
const text = leadingKeyText(value);
|
|
44
|
+
return text === undefined ? [] : [text];
|
|
45
|
+
}
|
|
@@ -2,6 +2,7 @@ import { readSimDynamoDbBatchReads, } from "./sim-dynamodb-batch-reads.js";
|
|
|
2
2
|
import { assertDistinctBatchItems } from "./sim-dynamodb-batch-request-items.js";
|
|
3
3
|
import { reachSimDynamoDbBatchTables, } from "./sim-dynamodb-batch-tables.js";
|
|
4
4
|
import { refuseUnsimulatedBatchReadInput } from "./sim-dynamodb-unsimulated-batch-input.js";
|
|
5
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
5
6
|
/**
|
|
6
7
|
* The BatchGetItem command.
|
|
7
8
|
*
|
|
@@ -27,6 +28,7 @@ export class SimDynamoDbBatchGetItem {
|
|
|
27
28
|
access: this.access,
|
|
28
29
|
operation: "BatchGetItem",
|
|
29
30
|
caller: options?.caller,
|
|
31
|
+
leadingKeys: (entry) => simDynamoDbLeadingKeysOf(() => entry.keys),
|
|
30
32
|
});
|
|
31
33
|
const responses = Object.fromEntries(reached.map((entry) => [entry.requested.reference, readItems(entry)]));
|
|
32
34
|
// Nothing here throttles or stops at a response size, so no key is ever
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
|
|
2
2
|
import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
|
|
3
3
|
import type { SimDynamoDbTableAccess } from "../table/sim-dynamodb-table-access.js";
|
|
4
|
+
import type { SimDynamoDbLeadingKeys } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
4
5
|
/**
|
|
5
6
|
* What one table of a batch asks for, against the table it names.
|
|
6
7
|
*/
|
|
@@ -8,10 +9,17 @@ export interface SimDynamoDbBatchTable<Requested> {
|
|
|
8
9
|
readonly table: SimDynamoDbTable;
|
|
9
10
|
readonly requested: Requested;
|
|
10
11
|
}
|
|
11
|
-
interface SimDynamoDbBatchReach {
|
|
12
|
+
interface SimDynamoDbBatchReach<Requested> {
|
|
12
13
|
readonly access: SimDynamoDbTableAccess;
|
|
13
14
|
readonly operation: string;
|
|
14
15
|
readonly caller: SimAwsCaller | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* How to read the partition key values one table of the batch is asked for.
|
|
18
|
+
*
|
|
19
|
+
* Each table is authorized on its own, so each carries the keys the batch
|
|
20
|
+
* names in that table and no others.
|
|
21
|
+
*/
|
|
22
|
+
readonly leadingKeys: (requested: Requested) => SimDynamoDbLeadingKeys;
|
|
15
23
|
}
|
|
16
24
|
/**
|
|
17
25
|
* Reach every table a batch names, authorizing the caller against each.
|
|
@@ -24,5 +32,5 @@ interface SimDynamoDbBatchReach {
|
|
|
24
32
|
*/
|
|
25
33
|
export declare function reachSimDynamoDbBatchTables<Requested extends {
|
|
26
34
|
readonly reference: string;
|
|
27
|
-
}>(requested: readonly Requested[], reach: SimDynamoDbBatchReach): readonly SimDynamoDbBatchTable<Requested>[];
|
|
35
|
+
}>(requested: readonly Requested[], reach: SimDynamoDbBatchReach<Requested>): readonly SimDynamoDbBatchTable<Requested>[];
|
|
28
36
|
export {};
|
|
@@ -11,7 +11,7 @@ import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
|
|
|
11
11
|
export function reachSimDynamoDbBatchTables(requested, reach) {
|
|
12
12
|
const action = `dynamodb:${reach.operation}`;
|
|
13
13
|
const reached = requested.map((entry) => ({
|
|
14
|
-
table: reach.access.required(action, entry.reference, reach.caller),
|
|
14
|
+
table: reach.access.required(action, entry.reference, reach.caller, reach.leadingKeys(entry)),
|
|
15
15
|
requested: entry,
|
|
16
16
|
}));
|
|
17
17
|
assertDistinctTables(reached, reach.operation);
|
|
@@ -2,6 +2,7 @@ import { assertDistinctBatchItems } from "./sim-dynamodb-batch-request-items.js"
|
|
|
2
2
|
import { reachSimDynamoDbBatchTables } from "./sim-dynamodb-batch-tables.js";
|
|
3
3
|
import { readSimDynamoDbBatchWrites } from "./sim-dynamodb-batch-writes.js";
|
|
4
4
|
import { refuseUnsimulatedBatchWriteInput } from "./sim-dynamodb-unsimulated-batch-input.js";
|
|
5
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
5
6
|
const operation = "BatchWriteItem";
|
|
6
7
|
/**
|
|
7
8
|
* The BatchWriteItem command.
|
|
@@ -25,7 +26,12 @@ export class SimDynamoDbBatchWriteItem {
|
|
|
25
26
|
handle(command, options) {
|
|
26
27
|
const input = command.input;
|
|
27
28
|
refuseUnsimulatedBatchWriteInput(input);
|
|
28
|
-
const reached = reachSimDynamoDbBatchTables(readSimDynamoDbBatchWrites(input.RequestItems), {
|
|
29
|
+
const reached = reachSimDynamoDbBatchTables(readSimDynamoDbBatchWrites(input.RequestItems), {
|
|
30
|
+
access: this.access,
|
|
31
|
+
operation,
|
|
32
|
+
caller: options?.caller,
|
|
33
|
+
leadingKeys: (entry) => simDynamoDbLeadingKeysOf(() => entry.writes.map((write) => write.names)),
|
|
34
|
+
});
|
|
29
35
|
// Marshalling every key checks it against the table's key schema, and is
|
|
30
36
|
// what tells two operations on one item apart. Every table is checked
|
|
31
37
|
// before any of them is written to.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
|
|
1
2
|
import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
|
|
2
3
|
import type { SimDynamoDbWriteRequest } from "./batch.command.js";
|
|
3
4
|
/**
|
|
@@ -8,6 +9,11 @@ import type { SimDynamoDbWriteRequest } from "./batch.command.js";
|
|
|
8
9
|
* before it applies anything, since the check and the write read the same item.
|
|
9
10
|
*/
|
|
10
11
|
export interface SimDynamoDbBatchWrite {
|
|
12
|
+
/**
|
|
13
|
+
* The item or key this write names, which is where a policy condition reads
|
|
14
|
+
* its partition key value from.
|
|
15
|
+
*/
|
|
16
|
+
readonly names: SimDynamoDbItem;
|
|
11
17
|
/**
|
|
12
18
|
* The primary key this write works on, as the table marshals it.
|
|
13
19
|
*/
|
|
@@ -5,15 +5,15 @@ import { readSimDynamoDbKey } from "../item/sim-dynamodb-key-input.js";
|
|
|
5
5
|
* A put in a batch write, which replaces the whole item as PutItem does.
|
|
6
6
|
*/
|
|
7
7
|
class SimDynamoDbBatchPut {
|
|
8
|
-
|
|
8
|
+
names;
|
|
9
9
|
constructor(item) {
|
|
10
|
-
this.
|
|
10
|
+
this.names = item;
|
|
11
11
|
}
|
|
12
12
|
keyIn(table) {
|
|
13
|
-
return table.keyOfItem(this.
|
|
13
|
+
return table.keyOfItem(this.names);
|
|
14
14
|
}
|
|
15
15
|
applyTo(table) {
|
|
16
|
-
table.putItem(this.
|
|
16
|
+
table.putItem(this.names);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -21,15 +21,15 @@ class SimDynamoDbBatchPut {
|
|
|
21
21
|
* that is already free is deleted successfully.
|
|
22
22
|
*/
|
|
23
23
|
class SimDynamoDbBatchDelete {
|
|
24
|
-
|
|
24
|
+
names;
|
|
25
25
|
constructor(key) {
|
|
26
|
-
this.
|
|
26
|
+
this.names = key;
|
|
27
27
|
}
|
|
28
28
|
keyIn(table) {
|
|
29
|
-
return table.keyOfKey(this.
|
|
29
|
+
return table.keyOfKey(this.names);
|
|
30
30
|
}
|
|
31
31
|
applyTo(table) {
|
|
32
|
-
table.deleteItem(this.
|
|
32
|
+
table.deleteItem(this.names);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
1
2
|
import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
|
|
2
3
|
import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
|
|
3
4
|
import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
|
|
@@ -26,7 +27,7 @@ export class SimDynamoDbDeleteItem {
|
|
|
26
27
|
// The condition is read before the table is reached, so an expression
|
|
27
28
|
// DynamoDB would refuse is refused whether or not the key holds anything.
|
|
28
29
|
const check = SimDynamoDbConditionCheck.read(input, "DeleteItem");
|
|
29
|
-
const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller);
|
|
30
|
+
const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
|
|
30
31
|
const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "DeleteItem");
|
|
31
32
|
const key = readSimDynamoDbKey(input.Key);
|
|
32
33
|
check.assertHoldsFor(table.getItem(key));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
1
2
|
import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
|
|
2
3
|
import { refuseUnsimulatedItemReadInput } from "./sim-dynamodb-unsimulated-item-read-input.js";
|
|
3
4
|
import { readSimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection-expression.js";
|
|
@@ -26,7 +27,7 @@ export class SimDynamoDbGetItem {
|
|
|
26
27
|
// The projection is read before the table is reached, so an expression
|
|
27
28
|
// DynamoDB would refuse is refused whether or not the key holds anything.
|
|
28
29
|
const projection = readSimDynamoDbProjection(input);
|
|
29
|
-
const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller);
|
|
30
|
+
const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
|
|
30
31
|
const found = table.getItem(readSimDynamoDbKey(input.Key));
|
|
31
32
|
if (found === undefined) {
|
|
32
33
|
return { $metadata: {} };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
|
|
2
2
|
import { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
|
|
3
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
3
4
|
import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
|
|
4
5
|
import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
|
|
5
6
|
import { refuseUnsimulatedItemWriteInput } from "./sim-dynamodb-unsimulated-item-write-input.js";
|
|
@@ -36,7 +37,7 @@ export class SimDynamoDbPutItem {
|
|
|
36
37
|
// The condition is read before the table is reached, so an expression
|
|
37
38
|
// DynamoDB would refuse is refused whether or not the key holds anything.
|
|
38
39
|
const check = SimDynamoDbConditionCheck.read(input, "PutItem");
|
|
39
|
-
const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller);
|
|
40
|
+
const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readItem(input)]));
|
|
40
41
|
const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "PutItem");
|
|
41
42
|
const item = readItem(input);
|
|
42
43
|
check.assertHoldsFor(table.itemUnder(item));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
1
2
|
import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
|
|
2
3
|
import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
|
|
3
4
|
import { refuseUnsimulatedItemUpdateInput } from "./sim-dynamodb-unsimulated-item-update-input.js";
|
|
@@ -28,7 +29,7 @@ export class SimDynamoDbUpdateItem {
|
|
|
28
29
|
// The expressions are read before the table is reached, so one DynamoDB
|
|
29
30
|
// would refuse is refused whether or not the key holds anything.
|
|
30
31
|
const plan = SimDynamoDbUpdatePlan.read(input, "UpdateItem");
|
|
31
|
-
const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller);
|
|
32
|
+
const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
|
|
32
33
|
const asked = SimDynamoDbReturnValues.readForUpdate(input.ReturnValues, "UpdateItem");
|
|
33
34
|
const key = readSimDynamoDbKey(input.Key);
|
|
34
35
|
const existing = table.getItem(key);
|
|
@@ -2,6 +2,7 @@ import { SimDynamoDbItemPage } from "../item/sim-dynamodb-item-page.js";
|
|
|
2
2
|
import { SimDynamoDbReadAnswer } from "../read/sim-dynamodb-read-answer.js";
|
|
3
3
|
import { assertSimDynamoDbConsistentReadAnswerable } from "../read/sim-dynamodb-consistent-read.js";
|
|
4
4
|
import { SimDynamoDbSelect } from "../read/sim-dynamodb-select.js";
|
|
5
|
+
import { simDynamoDbLeadingKeyOf } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
5
6
|
import { readSimDynamoDbQueryExpressions } from "./sim-dynamodb-query-expressions.js";
|
|
6
7
|
import { refuseSimDynamoDbQuerySegment } from "./sim-dynamodb-query-segment.js";
|
|
7
8
|
import { readSimDynamoDbQueryStartKey } from "./sim-dynamodb-query-start-key.js";
|
|
@@ -30,7 +31,11 @@ export class SimDynamoDbQuery {
|
|
|
30
31
|
refuseUnsimulatedQueryInput(input);
|
|
31
32
|
refuseSimDynamoDbQuerySegment(input);
|
|
32
33
|
const expressions = readSimDynamoDbQueryExpressions(input);
|
|
33
|
-
|
|
34
|
+
// The one partition key the query reads is what authorization needs, and
|
|
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.
|
|
37
|
+
const table = this.access.required("dynamodb:Query", input.TableName, options?.caller, (reached) => simDynamoDbLeadingKeyOf(expressions.terms.forTable(reached.view(input.IndexName))
|
|
38
|
+
.partitionKeyValue));
|
|
34
39
|
// What is being read is settled here: the table, or one of its indexes. An
|
|
35
40
|
// IndexName the table does not have is refused rather than read as the
|
|
36
41
|
// table. Everything after this asks the view rather than the table, so a
|
|
@@ -4,6 +4,7 @@ import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
|
|
|
4
4
|
import type { SimDynamoDbTableName } from "../../table/sim-dynamodb-table-name.js";
|
|
5
5
|
import type { SimDynamoDbTableStore } from "../../table/sim-dynamodb-table-store.js";
|
|
6
6
|
import type { SimDynamoDbAuthorizer } from "../authorize/sim-dynamodb-authorizer.js";
|
|
7
|
+
import type { SimDynamoDbLeadingKeys } from "../authorize/sim-dynamodb-leading-keys.js";
|
|
7
8
|
interface SimDynamoDbTableAccessProperties {
|
|
8
9
|
readonly tables: SimDynamoDbTableStore;
|
|
9
10
|
readonly authorizer: SimDynamoDbAuthorizer;
|
|
@@ -14,9 +15,14 @@ interface SimDynamoDbTableAccessProperties {
|
|
|
14
15
|
*
|
|
15
16
|
* Every table command goes through the same two steps in the same order: read
|
|
16
17
|
* the name or ARN the request carries, then authorize the caller against it
|
|
17
|
-
* before
|
|
18
|
-
* order the same for all of them, so no command can
|
|
19
|
-
* unauthorized caller which table names are taken.
|
|
18
|
+
* before the command is told whether the table is there. Keeping them here is
|
|
19
|
+
* what makes that order the same for all of them, so no command can
|
|
20
|
+
* accidentally tell an unauthorized caller which table names are taken.
|
|
21
|
+
*
|
|
22
|
+
* A command reaching particular items hands over how to read their partition
|
|
23
|
+
* key values, which authorization needs the table to read. The table is found
|
|
24
|
+
* first for that, and a caller refused against a table that is not there still
|
|
25
|
+
* hears AccessDenied rather than a missing table.
|
|
20
26
|
*/
|
|
21
27
|
export declare class SimDynamoDbTableAccess {
|
|
22
28
|
private readonly tables;
|
|
@@ -28,13 +34,17 @@ export declare class SimDynamoDbTableAccess {
|
|
|
28
34
|
*/
|
|
29
35
|
reference(tableName: string | undefined): SimDynamoDbTableName;
|
|
30
36
|
/**
|
|
31
|
-
* Find the table a request names, refusing the caller before
|
|
37
|
+
* Find the table a request names, refusing the caller before answering.
|
|
32
38
|
*/
|
|
33
|
-
required(action: string, tableName: string | undefined, caller: SimAwsCaller | undefined): SimDynamoDbTable;
|
|
39
|
+
required(action: string, tableName: string | undefined, caller: SimAwsCaller | undefined, leadingKeys?: SimDynamoDbLeadingKeys): SimDynamoDbTable;
|
|
34
40
|
/**
|
|
35
41
|
* Find a table by a name that has already been read.
|
|
42
|
+
*
|
|
43
|
+
* A caller with no permission hears AccessDenied whether or not the table is
|
|
44
|
+
* there, which keeps an unauthorized caller from finding out which table
|
|
45
|
+
* names are taken.
|
|
36
46
|
*/
|
|
37
|
-
requiredByName(action: string, name: SimDynamoDbTableName, caller: SimAwsCaller | undefined): SimDynamoDbTable;
|
|
47
|
+
requiredByName(action: string, name: SimDynamoDbTableName, caller: SimAwsCaller | undefined, leadingKeys?: SimDynamoDbLeadingKeys): SimDynamoDbTable;
|
|
38
48
|
/**
|
|
39
49
|
* Ensure the caller may perform an action that names no particular table.
|
|
40
50
|
*/
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import { SimDynamoDbResourceNotFoundException } from "../../error/dynamodb.error.js";
|
|
2
2
|
import { readSimDynamoDbTableReference } from "../../table/sim-dynamodb-table-reference.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read the partition key values a request reaches, or none where they cannot
|
|
5
|
+
* be read.
|
|
6
|
+
*
|
|
7
|
+
* Authorization runs ahead of the checks a command makes on what it was given,
|
|
8
|
+
* as it does on AWS. A request whose key or key condition is malformed is
|
|
9
|
+
* authorized carrying no values and refused by the check that follows, so
|
|
10
|
+
* whatever reading them throws is dropped here.
|
|
11
|
+
*/
|
|
12
|
+
function readLeadingKeys(leadingKeys, table) {
|
|
13
|
+
try {
|
|
14
|
+
return leadingKeys?.(table);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* How a command reaches the table a request names.
|
|
5
22
|
*
|
|
6
23
|
* Every table command goes through the same two steps in the same order: read
|
|
7
24
|
* the name or ARN the request carries, then authorize the caller against it
|
|
8
|
-
* before
|
|
9
|
-
* order the same for all of them, so no command can
|
|
10
|
-
* unauthorized caller which table names are taken.
|
|
25
|
+
* before the command is told whether the table is there. Keeping them here is
|
|
26
|
+
* what makes that order the same for all of them, so no command can
|
|
27
|
+
* accidentally tell an unauthorized caller which table names are taken.
|
|
28
|
+
*
|
|
29
|
+
* A command reaching particular items hands over how to read their partition
|
|
30
|
+
* key values, which authorization needs the table to read. The table is found
|
|
31
|
+
* first for that, and a caller refused against a table that is not there still
|
|
32
|
+
* hears AccessDenied rather than a missing table.
|
|
11
33
|
*/
|
|
12
34
|
export class SimDynamoDbTableAccess {
|
|
13
35
|
tables;
|
|
@@ -25,17 +47,21 @@ export class SimDynamoDbTableAccess {
|
|
|
25
47
|
return readSimDynamoDbTableReference(tableName, this.accountRegionScope);
|
|
26
48
|
}
|
|
27
49
|
/**
|
|
28
|
-
* Find the table a request names, refusing the caller before
|
|
50
|
+
* Find the table a request names, refusing the caller before answering.
|
|
29
51
|
*/
|
|
30
|
-
required(action, tableName, caller) {
|
|
31
|
-
return this.requiredByName(action, this.reference(tableName), caller);
|
|
52
|
+
required(action, tableName, caller, leadingKeys) {
|
|
53
|
+
return this.requiredByName(action, this.reference(tableName), caller, leadingKeys);
|
|
32
54
|
}
|
|
33
55
|
/**
|
|
34
56
|
* Find a table by a name that has already been read.
|
|
57
|
+
*
|
|
58
|
+
* A caller with no permission hears AccessDenied whether or not the table is
|
|
59
|
+
* there, which keeps an unauthorized caller from finding out which table
|
|
60
|
+
* names are taken.
|
|
35
61
|
*/
|
|
36
|
-
requiredByName(action, name, caller) {
|
|
37
|
-
this.authorizer.authorizeTable(action, name.value, caller);
|
|
62
|
+
requiredByName(action, name, caller, leadingKeys) {
|
|
38
63
|
const table = this.tables.find(name);
|
|
64
|
+
this.authorizer.authorizeTable(action, name.value, caller, table === undefined ? undefined : readLeadingKeys(leadingKeys, table));
|
|
39
65
|
if (table === undefined) {
|
|
40
66
|
throw new SimDynamoDbResourceNotFoundException(`No DynamoDB Table named ${name.value}`);
|
|
41
67
|
}
|
|
@@ -15,7 +15,10 @@ export interface SimIamConditionOperator {
|
|
|
15
15
|
* there is none for the policy value to equal.
|
|
16
16
|
*
|
|
17
17
|
* A `ForAnyValue` operator answers false whatever it wraps, because no
|
|
18
|
-
* request value is there to satisfy it.
|
|
18
|
+
* request value is there to satisfy it. A `ForAllValues` operator answers
|
|
19
|
+
* true, because every value the request carries matches vacuously. AWS
|
|
20
|
+
* documents all three rules, and warns that the `ForAllValues` rule leaves
|
|
21
|
+
* an `Allow` overly permissive without a `Null` guard beside it.
|
|
19
22
|
*/
|
|
20
23
|
readonly matchesAbsentKey: boolean;
|
|
21
24
|
/**
|
|
@@ -2,9 +2,13 @@ import type { SimIamConditionValue } from "../../../../../policy/sim-iam-policy.
|
|
|
2
2
|
import type { SimIamConditionOperator } from "../../sim-iam-condition-operator.js";
|
|
3
3
|
/**
|
|
4
4
|
* Base for `ForAllValues` IAM string operators.
|
|
5
|
+
*
|
|
6
|
+
* An absent context key matches. AWS answers true where the request carries
|
|
7
|
+
* no value for the key. A `ForAllValues` `Allow` therefore needs a `Null`
|
|
8
|
+
* guard beside it to stay tight.
|
|
5
9
|
*/
|
|
6
10
|
export declare abstract class SimIamForAllValuesStringConditionOperator implements SimIamConditionOperator {
|
|
7
|
-
readonly matchesAbsentKey =
|
|
11
|
+
readonly matchesAbsentKey = true;
|
|
8
12
|
/**
|
|
9
13
|
* Check whether a given value matches the expected value.
|
|
10
14
|
*/
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { simIamStringValues } from "../sim-iam-string-values.js";
|
|
2
2
|
/**
|
|
3
3
|
* Base for `ForAllValues` IAM string operators.
|
|
4
|
+
*
|
|
5
|
+
* An absent context key matches. AWS answers true where the request carries
|
|
6
|
+
* no value for the key. A `ForAllValues` `Allow` therefore needs a `Null`
|
|
7
|
+
* guard beside it to stay tight.
|
|
4
8
|
*/
|
|
5
9
|
export class SimIamForAllValuesStringConditionOperator {
|
|
6
|
-
matchesAbsentKey =
|
|
10
|
+
matchesAbsentKey = true;
|
|
7
11
|
/**
|
|
8
12
|
* Check whether a given value matches the expected value.
|
|
9
13
|
*/
|
|
@@ -58,7 +58,7 @@ export class SimWafAssociations {
|
|
|
58
58
|
if (association === undefined) {
|
|
59
59
|
return undefined;
|
|
60
60
|
}
|
|
61
|
-
return association.webAcl.evaluate(simWafInspectedRequest(request.request, request.body));
|
|
61
|
+
return association.webAcl.evaluate(simWafInspectedRequest(request.request, request.body, association.webAcl.bodyInspectionLimitBytes(association.resourceType)));
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Forget whatever is in front of one resource.
|
|
@@ -32,6 +32,7 @@ export class SimCfnWafWebAclConfig extends SimCfnWafResourceConfig {
|
|
|
32
32
|
Rules: this.rules(),
|
|
33
33
|
VisibilityConfig: this.value("VisibilityConfig"),
|
|
34
34
|
CustomResponseBodies: this.value("CustomResponseBodies"),
|
|
35
|
+
AssociationConfig: this.value("AssociationConfig"),
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
@@ -6,12 +6,6 @@ const refusedMembers = [
|
|
|
6
6
|
[(input) => input.CaptchaConfig, "CaptchaConfig", tokenActions],
|
|
7
7
|
[(input) => input.ChallengeConfig, "ChallengeConfig", tokenActions],
|
|
8
8
|
[(input) => input.TokenDomains, "TokenDomains", tokenActions],
|
|
9
|
-
[
|
|
10
|
-
(input) => input.AssociationConfig,
|
|
11
|
-
"AssociationConfig",
|
|
12
|
-
"it sets the body size a web ACL inspects for the resources it is " +
|
|
13
|
-
"associated with, and nothing is associated with a web ACL yet",
|
|
14
|
-
],
|
|
15
9
|
[
|
|
16
10
|
(input) => input.DataProtectionConfig,
|
|
17
11
|
"DataProtectionConfig",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SimResponseMetadata } from "../../../aws/metadata/response-metadata.type.js";
|
|
2
2
|
import type { SimWafActionInput } from "../../web-acl/sim-waf-action.type.js";
|
|
3
|
+
import type { SimWafAssociationConfigInput } from "../../web-acl/sim-waf-association-config.js";
|
|
3
4
|
import type { SimWafCustomResponseBodies } from "../../web-acl/sim-waf-custom-response.type.js";
|
|
4
5
|
import type { SimWafRuleInput } from "../../web-acl/sim-waf-rule.type.js";
|
|
5
6
|
/**
|
|
@@ -27,7 +28,7 @@ export interface SimWafWebAclWriteInput {
|
|
|
27
28
|
readonly CaptchaConfig?: unknown;
|
|
28
29
|
readonly ChallengeConfig?: unknown;
|
|
29
30
|
readonly TokenDomains?: readonly string[] | undefined;
|
|
30
|
-
readonly AssociationConfig?:
|
|
31
|
+
readonly AssociationConfig?: SimWafAssociationConfigInput | undefined;
|
|
31
32
|
readonly DataProtectionConfig?: unknown;
|
|
32
33
|
readonly OnSourceDDoSProtectionConfig?: unknown;
|
|
33
34
|
readonly ApplicationConfig?: unknown;
|
|
@@ -72,6 +73,7 @@ export interface SimWafWebAclOutput {
|
|
|
72
73
|
readonly Rules: readonly SimWafRuleInput[];
|
|
73
74
|
readonly VisibilityConfig: unknown;
|
|
74
75
|
readonly CustomResponseBodies: SimWafCustomResponseBodies | undefined;
|
|
76
|
+
readonly AssociationConfig: SimWafAssociationConfigInput | undefined;
|
|
75
77
|
}
|
|
76
78
|
export interface SimGetWebAclCommandOutput {
|
|
77
79
|
readonly WebACL?: SimWafWebAclOutput | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SimWafBodyInspectionResourceType } from "../web-acl/sim-waf-association-config.js";
|
|
1
2
|
/**
|
|
2
3
|
* What a web ACL is asked to decide about.
|
|
3
4
|
*/
|
|
@@ -10,4 +11,11 @@ export interface SimWafEvaluationRequest {
|
|
|
10
11
|
* stream that cannot be read twice, so it is passed in already buffered.
|
|
11
12
|
*/
|
|
12
13
|
readonly body?: Uint8Array | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The type of resource the request reached, when it reached one.
|
|
16
|
+
*
|
|
17
|
+
* Only the body inspection limit reads it, and a web ACL evaluated on its
|
|
18
|
+
* own reads the default limit for every resource type.
|
|
19
|
+
*/
|
|
20
|
+
readonly resourceType?: SimWafBodyInspectionResourceType | undefined;
|
|
13
21
|
}
|