Skip to content

Commit 4efdb7e

Browse files
committed
fix(middleware-ssec): add logic to handle string input as specified by api model
1 parent a54f8c4 commit 4efdb7e

File tree

2 files changed

+66
-17
lines changed

2 files changed

+66
-17
lines changed

packages/middleware-ssec/src/index.spec.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { ChecksumConstructor } from "@smithy/types";
2+
import * as crypto from "crypto";
23

34
import { ssecMiddleware } from "./";
45

56
describe("ssecMiddleware", () => {
67
const next = jest.fn();
78
const decoder = jest.fn().mockResolvedValue(new Uint8Array(0));
8-
const encoder = jest.fn().mockReturnValue("base64");
9+
const encoder1 = jest.fn();
10+
const encoder2 = jest.fn();
911
const mockHashUpdate = jest.fn();
1012
const mockHashReset = jest.fn();
1113
const mockHashDigest = jest.fn().mockReturnValue(new Uint8Array(0));
@@ -17,13 +19,53 @@ describe("ssecMiddleware", () => {
1719
beforeEach(() => {
1820
next.mockClear();
1921
decoder.mockClear();
20-
encoder.mockClear();
22+
encoder1.mockClear();
23+
encoder2.mockClear();
2124
mockHashUpdate.mockClear();
2225
mockHashDigest.mockClear();
2326
mockHashReset.mockClear();
2427
});
2528

2629
it("should base64 encode input keys and set respective MD5 inputs", async () => {
30+
encoder1.mockReturnValue("/+JF8FMG8UVMWSaNz0s6Wg==");
31+
const key = "TestKey123";
32+
const binaryRepresentationOfKey = Buffer.from(key);
33+
const base64Key = binaryRepresentationOfKey.toString("base64");
34+
const md5Hash = crypto.createHash("md5").update(binaryRepresentationOfKey).digest();
35+
const base64Md5Hash = Buffer.from(md5Hash).toString("base64");
36+
37+
const args = {
38+
input: {
39+
SSECustomerKey: base64Key,
40+
CopySourceSSECustomerKey: base64Key,
41+
},
42+
};
43+
44+
const handler = ssecMiddleware({
45+
base64Encoder: encoder1,
46+
utf8Decoder: decoder,
47+
md5: MockHash,
48+
})(next, {} as any);
49+
50+
await handler(args);
51+
52+
expect(next.mock.calls.length).toBe(1);
53+
expect(next).toHaveBeenCalledWith({
54+
input: {
55+
SSECustomerKey: base64Key,
56+
SSECustomerKeyMD5: base64Md5Hash,
57+
CopySourceSSECustomerKey: base64Key,
58+
CopySourceSSECustomerKeyMD5: base64Md5Hash,
59+
},
60+
});
61+
expect(decoder.mock.calls.length).toBe(0);
62+
expect(encoder1.mock.calls.length).toBe(2);
63+
expect(mockHashUpdate.mock.calls.length).toBe(2);
64+
expect(mockHashDigest.mock.calls.length).toBe(2);
65+
encoder1.mockClear();
66+
});
67+
it("should base64 encode input keys and set respective MD5 inputs", async () => {
68+
encoder2.mockReturnValue("base64");
2769
const args = {
2870
input: {
2971
SSECustomerKey: "foo",
@@ -32,7 +74,7 @@ describe("ssecMiddleware", () => {
3274
};
3375

3476
const handler = ssecMiddleware({
35-
base64Encoder: encoder,
77+
base64Encoder: encoder2,
3678
utf8Decoder: decoder,
3779
md5: MockHash,
3880
})(next, {} as any);
@@ -49,8 +91,9 @@ describe("ssecMiddleware", () => {
4991
},
5092
});
5193
expect(decoder.mock.calls.length).toBe(2);
52-
expect(encoder.mock.calls.length).toBe(4);
94+
expect(encoder2.mock.calls.length).toBe(4);
5395
expect(mockHashUpdate.mock.calls.length).toBe(2);
5496
expect(mockHashDigest.mock.calls.length).toBe(2);
97+
encoder2.mockClear();
5598
});
5699
});

packages/middleware-ssec/src/index.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface PreviouslyResolved {
2121
export function ssecMiddleware(options: PreviouslyResolved): InitializeMiddleware<any, any> {
2222
return <Output extends MetadataBearer>(next: InitializeHandler<any, Output>): InitializeHandler<any, Output> =>
2323
async (args: InitializeHandlerArguments<any>): Promise<InitializeHandlerOutput<Output>> => {
24-
let input = { ...args.input };
24+
const input = { ...args.input };
2525
const properties = [
2626
{
2727
target: "SSECustomerKey",
@@ -36,19 +36,25 @@ export function ssecMiddleware(options: PreviouslyResolved): InitializeMiddlewar
3636
for (const prop of properties) {
3737
const value: SourceData | undefined = (input as any)[prop.target];
3838
if (value) {
39-
const valueView: Uint8Array = ArrayBuffer.isView(value)
40-
? new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
41-
: typeof value === "string"
42-
? options.utf8Decoder(value)
43-
: new Uint8Array(value);
44-
const encoded = options.base64Encoder(valueView);
39+
let valueForHash: Uint8Array;
40+
if (typeof value === "string") {
41+
const isBase64Encoded = /^(?:[A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
42+
if (isBase64Encoded) {
43+
valueForHash = new Uint8Array(Buffer.from(value, "base64"));
44+
} else {
45+
valueForHash = options.utf8Decoder(value);
46+
input[prop.target] = options.base64Encoder(valueForHash);
47+
}
48+
} else {
49+
valueForHash = ArrayBuffer.isView(value)
50+
? new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
51+
: new Uint8Array(value);
52+
input[prop.target] = options.base64Encoder(valueForHash);
53+
}
54+
4555
const hash = new options.md5();
46-
hash.update(valueView);
47-
input = {
48-
...(input as any),
49-
[prop.target]: encoded,
50-
[prop.hash]: options.base64Encoder(await hash.digest()),
51-
};
56+
hash.update(valueForHash);
57+
input[prop.hash] = options.base64Encoder(await hash.digest());
5258
}
5359
}
5460

0 commit comments

Comments
 (0)