Skip to content

Commit

Permalink
test(signature-v4-multi-region): add s3 e2e test
Browse files Browse the repository at this point in the history
  • Loading branch information
siddsriv committed Aug 21, 2024
1 parent 7bd6a61 commit 3dfab74
Showing 1 changed file with 193 additions and 0 deletions.
193 changes: 193 additions & 0 deletions packages/signature-v4-multi-region/src/s3-mrap-sigv4a.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import "@smithy/signature-v4a";

import { Sha256 } from "@aws-crypto/sha256-js";
import {
S3Client,
CreateBucketCommand,
DeleteBucketCommand,
PutObjectCommand,
ListObjectsV2Command
} from "@aws-sdk/client-s3";
import {
S3ControlClient,
CreateMultiRegionAccessPointCommand,
DeleteMultiRegionAccessPointCommand,
DescribeMultiRegionAccessPointOperationCommand,
GetMultiRegionAccessPointCommand
} from "@aws-sdk/client-s3-control";
import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
import { SignatureV4MultiRegion } from "@aws-sdk/signature-v4-multi-region";
import { HttpRequest } from "@smithy/protocol-http";

jest.setTimeout(1800000); // 30 minutes (MRAP operations can take a while)

describe("S3 Multi-Region Access Point with SignatureV4a (JS Implementation)", () => {
let s3Client: S3Client;
let s3ControlClient: S3ControlClient;
let accountId: string;
let signer: SignatureV4MultiRegion;
let mrapName: string;
let bucketName1: string;
let bucketName2: string;
let mrapArn: string;

beforeAll(async () => {
const stsClient = new STSClient({});
const { Account } = await stsClient.send(new GetCallerIdentityCommand({}));
accountId = Account!;
const timestamp = Date.now();
mrapName = `test-mrap-${timestamp}`;
bucketName1 = `test-bucket1-${timestamp}`;
bucketName2 = `test-bucket2-${timestamp}`;

signer = new SignatureV4MultiRegion({
service: "s3",
region: "*",
sha256: Sha256,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
sessionToken: process.env.AWS_SESSION_TOKEN,
},
});

s3Client = new S3Client({
region: "*",
useArnRegion: true,
signer,
});

s3ControlClient = new S3ControlClient({
region: "*",
signer,
});

// Create buckets
await s3Client.send(new CreateBucketCommand({ Bucket: bucketName1, CreateBucketConfiguration: { LocationConstraint: "us-west-2" } }));
await s3Client.send(new CreateBucketCommand({ Bucket: bucketName2, CreateBucketConfiguration: { LocationConstraint: "us-east-2" } }));

// Create MRAP
const createResponse = await s3ControlClient.send(
new CreateMultiRegionAccessPointCommand({
AccountId: accountId,
ClientToken: `create-${timestamp}`,
Details: {
Name: mrapName,
PublicAccessBlock: {
BlockPublicAcls: true,
BlockPublicPolicy: true,
IgnorePublicAcls: true,
RestrictPublicBuckets: true,
},
Regions: [
{ Bucket: bucketName1, BucketAccountId: accountId },
{ Bucket: bucketName2, BucketAccountId: accountId },
],
},
})
);

// Wait for MRAP to be created
let mrapReady = false;
let retries = 0;
while (!mrapReady && retries < 60) {
const describeResponse = await s3ControlClient.send(
new DescribeMultiRegionAccessPointOperationCommand({
AccountId: accountId,
RequestTokenARN: createResponse.RequestTokenARN,
})
);

if (describeResponse.AsyncOperation?.RequestStatus === "SUCCESS") {
mrapReady = true;
} else {
await new Promise(resolve => setTimeout(resolve, 30000)); // Wait for 30 seconds before retrying
retries++;
}
}

if (!mrapReady) {
throw new Error("MRAP creation timed out");
}

// Get MRAP ARN
const getResponse = await s3ControlClient.send(
new GetMultiRegionAccessPointCommand({
AccountId: accountId,
Name: mrapName,
})
);
mrapArn = getResponse.AccessPoint!.Alias!;

// Upload a small file to one of the buckets
await s3Client.send(new PutObjectCommand({
Bucket: bucketName1,
Key: "testfile",
Body: Buffer.from("test", "utf-8")
}));
});

afterAll(async () => {
// Delete MRAP
try {
await s3ControlClient.send(
new DeleteMultiRegionAccessPointCommand({
AccountId: accountId,
ClientToken: `delete-${Date.now()}`,
Details: {
Name: mrapName,
},
})
);
} catch (error) {
console.error("Failed to initiate deletion of Multi-Region Access Point:", error);
}

// Delete buckets
try {
await s3Client.send(new DeleteBucketCommand({ Bucket: bucketName1 }));
await s3Client.send(new DeleteBucketCommand({ Bucket: bucketName2 }));
} catch (error) {
console.error("Failed to delete buckets:", error);
}
});

it("should use SignatureV4a JS implementation", async () => {
const mockRequest = new HttpRequest({
method: "GET",
protocol: "https:",
hostname: "s3-global.amazonaws.com",
headers: {
host: "s3-global.amazonaws.com",
},
path: "/",
});

const signSpy = jest.spyOn(signer, "sign");

await signer.sign(mockRequest, { signingRegion: "*" });

expect(signSpy).toHaveBeenCalled();
const signArgs = signSpy.mock.calls[0];
expect(signArgs[1]?.signingRegion).toBe("*");

// verify that signed request has the expected SigV4a headers
const signedRequest = await signSpy.mock.results[0].value;
expect(signedRequest.headers["x-amz-region-set"]).toBe("*");
expect(signedRequest.headers["authorization"]).toContain("AWS4-ECDSA-P256-SHA256");

signSpy.mockRestore();
});

it("should list objects through MRAP using SignatureV4a", async () => {
const command = new ListObjectsV2Command({
Bucket: mrapArn,
});

const response = await s3Client.send(command);

expect(response.Contents).toBeDefined();
expect(response.Contents?.length).toBeGreaterThan(0);
expect(response.Contents?.some(object => object.Key === "testfile")).toBe(true);
});
});

0 comments on commit 3dfab74

Please sign in to comment.