Skip to content

Commit

Permalink
fix(middleware-sdk-rds): double encoding the presigned url (#2711)
Browse files Browse the repository at this point in the history
* fix(middleware-sdk-rds): double encoding the presigned url

* fix(middleware-sdk-rds): support StartDBInstanceAutomatedBackupsReplication api

According to codegen, StartDBInstanceAutomatedBackupsReplication API should be
supported with cross-region copy.

* fix(client-docdb): remove cross-region copy for non-applicable apis
  • Loading branch information
AllanZhengYP committed Aug 30, 2021
1 parent 5249df4 commit 8a271be
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 68 deletions.
2 changes: 0 additions & 2 deletions clients/client-docdb/commands/CreateDBClusterCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
deserializeAws_queryCreateDBClusterCommand,
serializeAws_queryCreateDBClusterCommand,
} from "../protocols/Aws_query";
import { getCrossRegionPresignedUrlPlugin } from "@aws-sdk/middleware-sdk-rds";
import { getSerdePlugin } from "@aws-sdk/middleware-serde";
import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http";
import { Command as $Command } from "@aws-sdk/smithy-client";
Expand Down Expand Up @@ -61,7 +60,6 @@ export class CreateDBClusterCommand extends $Command<
options?: __HttpHandlerOptions
): Handler<CreateDBClusterCommandInput, CreateDBClusterCommandOutput> {
this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize));
this.middlewareStack.use(getCrossRegionPresignedUrlPlugin(configuration));

const stack = clientStack.concat(this.middlewareStack);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,43 +18,41 @@
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin;
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.MapUtils;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
public class AddCrossRegionCopyingPlugin implements TypeScriptIntegration {
private static final Set<String> SHARED_PRESIGNED_URL_OPERATIONS = SetUtils.of(
"CopyDBClusterSnapshot",
"CreateDBCluster"
);
private static final Set<String> RDS_PRESIGNED_URL_OPERATIONS = SetUtils.of(
"CopyDBSnapshot",
"CreateDBInstanceReadReplica",
"StartDBInstanceAutomatedBackupsReplication"
private static final Map<String, Set<String>> PRESIGNED_URL_OPERATIONS_MAP = MapUtils.of(
"RDS", SetUtils.of(
"CopyDBClusterSnapshot",
"CreateDBCluster",
"CopyDBSnapshot",
"CreateDBInstanceReadReplica",
"StartDBInstanceAutomatedBackupsReplication"),
"DocDB", SetUtils.of("CopyDBClusterSnapshot"),
"Neptune", SetUtils.of("CopyDBClusterSnapshot", "CreateDBCluster")
);

@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.RDS_MIDDLEWARE.dependency, "CrossRegionPresignedUrl",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> RDS_PRESIGNED_URL_OPERATIONS.contains(o.getId().getName(s))
&& testServiceId(s, "RDS"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.RDS_MIDDLEWARE.dependency, "CrossRegionPresignedUrl",
HAS_MIDDLEWARE)
.operationPredicate((m, s, o) -> SHARED_PRESIGNED_URL_OPERATIONS.contains(o.getId().getName(s))
&& (testServiceId(s, "RDS") || testServiceId(s, "DocDB") || testServiceId(s, "Neptune")))
.build()
);
return PRESIGNED_URL_OPERATIONS_MAP.entrySet().stream().map((entry) -> {
String serviceId = entry.getKey();
Set<String> commands = entry.getValue();
return RuntimeClientPlugin.builder()
.withConventions(AwsDependency.RDS_MIDDLEWARE.dependency, "CrossRegionPresignedUrl", HAS_MIDDLEWARE)
.operationPredicate(
(m, s, o) -> commands.contains(o.getId().getName(s)) && testServiceId(s, serviceId))
.build();
}).collect(Collectors.toList());
}

private static boolean testServiceId(Shape serviceShape, String expectedId) {
Expand Down
1 change: 0 additions & 1 deletion packages/middleware-sdk-rds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"@aws-sdk/signature-v4": "3.25.0",
"@aws-sdk/types": "3.25.0",
"@aws-sdk/util-format-url": "3.25.0",
"@aws-sdk/util-uri-escape": "3.23.0",
"tslib": "^2.3.0"
},
"devDependencies": {
Expand Down
103 changes: 63 additions & 40 deletions packages/middleware-sdk-rds/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ describe("middleware-sdk-rds", () => {
expect(middlewareOutput.input.TargetDBSnapshotIdentifier).toEqual(params.TargetDBSnapshotIdentifier);
expect(middlewareOutput.input.KmsKeyId).toEqual(params.KmsKeyId);
const presignedUrl = middlewareOutput.input.PreSignedUrl;
expect(presignedUrl).toMatch(/https%3A%2F%2Frds\.src\-region\.amazonaws\.com%2F%3F/);
expect(presignedUrl).toMatch(/Action%3DCopyDBSnapshot/);
expect(presignedUrl).toMatch(/Version%3D2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token%3Dsession/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm%3DAWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders%3Dhost/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential%3D/);
expect(presignedUrl).toMatch(/X\-Amz\-Date%3D/);
expect(presignedUrl).toMatch(/X-Amz-Expires%3D([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature%3D000000/);
expect(presignedUrl).toMatch(/https\:\/\/rds\.src\-region\.amazonaws\.com\/\?/);
expect(presignedUrl).toMatch(/Action\=CopyDBSnapshot/);
expect(presignedUrl).toMatch(/Version\=2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token\=session/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm\=AWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders\=host/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential\=/);
expect(presignedUrl).toMatch(/X\-Amz\-Date\=/);
expect(presignedUrl).toMatch(/X-Amz-Expires=([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature=000000/);
});

it("should build CreateDBInstanceReadReplica cross origin presigned url correctly ", async () => {
Expand All @@ -57,16 +57,16 @@ describe("middleware-sdk-rds", () => {
expect(middlewareOutput.input.DBInstanceIdentifier).toEqual(params.DBInstanceIdentifier);
expect(middlewareOutput.input.KmsKeyId).toEqual(params.KmsKeyId);
const presignedUrl = middlewareOutput.input.PreSignedUrl;
expect(presignedUrl).toMatch(/https%3A%2F%2Frds\.src\-region\.amazonaws\.com%2F%3F/);
expect(presignedUrl).toMatch(/Action%3DCreateDBInstanceReadReplica/);
expect(presignedUrl).toMatch(/Version%3D2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token%3Dsession/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm%3DAWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders%3Dhost/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential%3D/);
expect(presignedUrl).toMatch(/X\-Amz\-Date%3D/);
expect(presignedUrl).toMatch(/X-Amz-Expires%3D([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature%3D000000/);
expect(presignedUrl).toMatch(/https\:\/\/rds\.src\-region\.amazonaws\.com\/\?/);
expect(presignedUrl).toMatch(/Action\=CreateDBInstanceReadReplica/);
expect(presignedUrl).toMatch(/Version\=2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token\=session/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm\=AWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders\=host/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential\=/);
expect(presignedUrl).toMatch(/X\-Amz\-Date\=/);
expect(presignedUrl).toMatch(/X-Amz-Expires=([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature=000000/);
});

it("should build CreateDBCluster cross origin presigned url correctly ", async () => {
Expand All @@ -82,16 +82,16 @@ describe("middleware-sdk-rds", () => {
expect(middlewareOutput.input.DBClusterIdentifier).toEqual(params.DBClusterIdentifier);
expect(middlewareOutput.input.KmsKeyId).toEqual(params.KmsKeyId);
const presignedUrl = middlewareOutput.input.PreSignedUrl;
expect(presignedUrl).toMatch(/https%3A%2F%2Frds\.src\-region\.amazonaws\.com%2F%3F/);
expect(presignedUrl).toMatch(/Action%3DCreateDBCluster/);
expect(presignedUrl).toMatch(/Version%3D2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token%3Dsession/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm%3DAWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders%3Dhost/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential%3D/);
expect(presignedUrl).toMatch(/X\-Amz\-Date%3D/);
expect(presignedUrl).toMatch(/X-Amz-Expires%3D([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature%3D000000/);
expect(presignedUrl).toMatch(/https\:\/\/rds\.src\-region\.amazonaws\.com\/\?/);
expect(presignedUrl).toMatch(/Action\=CreateDBCluster/);
expect(presignedUrl).toMatch(/Version\=2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token\=session/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm\=AWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders\=host/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential\=/);
expect(presignedUrl).toMatch(/X\-Amz\-Date\=/);
expect(presignedUrl).toMatch(/X-Amz-Expires=([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature=000000/);
});

it("should build CopyDBClusterSnapshot cross origin presigned url correctly ", async () => {
Expand All @@ -107,16 +107,39 @@ describe("middleware-sdk-rds", () => {
expect(middlewareOutput.input.TargetDBClusterSnapshotIdentifier).toEqual(params.TargetDBClusterSnapshotIdentifier);
expect(middlewareOutput.input.KmsKeyId).toEqual(params.KmsKeyId);
const presignedUrl = middlewareOutput.input.PreSignedUrl;
expect(presignedUrl).toMatch(/https%3A%2F%2Frds\.src\-region\.amazonaws\.com%2F%3F/);
expect(presignedUrl).toMatch(/Action%3DCopyDBClusterSnapshot/);
expect(presignedUrl).toMatch(/Version%3D2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token%3Dsession/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm%3DAWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders%3Dhost/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential%3D/);
expect(presignedUrl).toMatch(/X\-Amz\-Date%3D/);
expect(presignedUrl).toMatch(/X-Amz-Expires%3D([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature%3D000000/);
expect(presignedUrl).toMatch(/https\:\/\/rds\.src\-region\.amazonaws\.com\/\?/);
expect(presignedUrl).toMatch(/Action\=CopyDBClusterSnapshot/);
expect(presignedUrl).toMatch(/Version\=2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token\=session/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm\=AWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders\=host/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential\=/);
expect(presignedUrl).toMatch(/X\-Amz\-Date\=/);
expect(presignedUrl).toMatch(/X-Amz-Expires=([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature=000000/);
});

it("should build StartDBInstanceAutomatedBackupsReplication cross origin presigned url correctly ", async () => {
const params = {
SourceDBInstanceArn: arn,
KmsKeyId: "000-111",
};
await handler({ input: params });
expect(nextHandler.mock.calls.length).toBe(1);
const middlewareOutput = nextHandler.mock.calls[0][0];
expect(middlewareOutput.input.SourceDBInstanceArn).toEqual(params.SourceDBInstanceArn);
expect(middlewareOutput.input.KmsKeyId).toEqual(params.KmsKeyId);
const presignedUrl = middlewareOutput.input.PreSignedUrl;
expect(presignedUrl).toMatch(/https\:\/\/rds\.src\-region\.amazonaws\.com\/\?/);
expect(presignedUrl).toMatch(/Action\=StartDBInstanceAutomatedBackupsReplication/);
expect(presignedUrl).toMatch(/Version\=2014\-10\-31/);
expect(presignedUrl).toMatch(/X\-Amz\-Security\-Token\=session/);
expect(presignedUrl).toMatch(/X\-Amz\-Algorithm\=AWS4\-HMAC\-SHA256/);
expect(presignedUrl).toMatch(/X\-Amz\-SignedHeaders\=host/);
expect(presignedUrl).toMatch(/X\-Amz\-Credential\=/);
expect(presignedUrl).toMatch(/X\-Amz\-Date\=/);
expect(presignedUrl).toMatch(/X-Amz-Expires=([\d]+)/);
expect(presignedUrl).toMatch(/X-Amz-Signature=000000/);
});

it("should not generate PreSignedUrl if source identifier is not ARN", async () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/middleware-sdk-rds/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
Provider,
} from "@aws-sdk/types";
import { formatUrl } from "@aws-sdk/util-format-url";
import { escapeUri } from "@aws-sdk/util-uri-escape";

const regARN = /arn:[\w+=/,.@-]+:[\w+=/,.@-]+:([\w+=/,.@-]*)?:[0-9]+:[\w+=/,.@-]+(:[\w+=/,.@-]+)?(:[\w+=/,.@-]+)?/;

Expand All @@ -23,13 +22,15 @@ const sourceIds: string[] = [
"SourceDBInstanceIdentifier",
"ReplicationSourceIdentifier",
"SourceDBClusterSnapshotIdentifier",
"SourceDBInstanceArn",
];

const sourceIdToCommandKeyMap: { [key: string]: string } = {
SourceDBSnapshotIdentifier: "CopyDBSnapshot",
SourceDBInstanceIdentifier: "CreateDBInstanceReadReplica",
ReplicationSourceIdentifier: "CreateDBCluster",
SourceDBClusterSnapshotIdentifier: "CopyDBClusterSnapshot",
SourceDBInstanceArn: "StartDBInstanceAutomatedBackupsReplication",
};

const version = "2014-10-31";
Expand Down Expand Up @@ -94,7 +95,7 @@ export function crossRegionPresignedUrlMiddleware(options: PreviouslyResolved):
...args,
input: {
...args.input,
PreSignedUrl: escapeUri(formatUrl(presignedRequest)),
PreSignedUrl: formatUrl(presignedRequest),
},
};
}
Expand Down

0 comments on commit 8a271be

Please sign in to comment.