Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions packages/cloudfront-signer/src/sign.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,3 +604,167 @@ describe("getSignedCookies", () => {
expect(verifySignature(denormalizeBase64(result["CloudFront-Signature"]), policy)).toBeTruthy();
});
});

describe("getSignedUrl- when signing a URL with a date range", () => {
const dateString = "2024-05-17T12:30:45.000Z";
const dateGreaterThanString = "2024-05-16T12:30:45.000Z";
const dateNumber = 1125674245900;
const dateGreaterThanNumber = 1716034245000;
const dateObject = new Date(dateString);
const dateGreaterThanObject = new Date(dateGreaterThanString);
it("allows string input compatible with Date constructor", () => {
const epochDateLessThan = Math.round(new Date(dateString).getTime() / 1000);
const resultUrl = getSignedUrl({
url,
keyPairId,
dateLessThan: dateString,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are two ends to the date range options, right? this needs to test the other end too.

privateKey,
passphrase,
});
const resultCookies = getSignedCookies({
url,
keyPairId,
dateLessThan: dateString,
privateKey,
passphrase,
});

expect(resultUrl).toContain(`Expires=${epochDateLessThan}`);
expect(resultCookies["CloudFront-Expires"]).toBe(epochDateLessThan);
});

it("allows number input in milliseconds compatible with Date constructor", () => {
const epochDateLessThan = Math.round(new Date(dateNumber).getTime() / 1000);
const resultUrl = getSignedUrl({
url,
keyPairId,
dateLessThan: dateNumber as unknown as string,
privateKey,
passphrase,
});
const resultCookies = getSignedCookies({
url,
keyPairId,
dateLessThan: dateNumber as unknown as string,
privateKey,
passphrase,
});

expect(resultUrl).toContain(`Expires=${epochDateLessThan}`);
expect(resultCookies["CloudFront-Expires"]).toBe(epochDateLessThan);
});
it("allows Date object input", () => {
const epochDateLessThan = Math.round(dateObject.getTime() / 1000);
const resultUrl = getSignedUrl({
url,
keyPairId,
dateLessThan: dateObject as unknown as string,
privateKey,
passphrase,
});
const resultCookies = getSignedCookies({
url,
keyPairId,
dateLessThan: dateObject as unknown as string,
privateKey,
passphrase,
});

expect(resultUrl).toContain(`Expires=${epochDateLessThan}`);
expect(resultCookies["CloudFront-Expires"]).toBe(epochDateLessThan);
});
it("allows string input for date range", () => {
const result = getSignedUrl({
url,
keyPairId,
dateLessThan: dateString,
dateGreaterThan: dateGreaterThanString,
privateKey,
passphrase,
});

const policyStr = JSON.stringify({
Statement: [
{
Resource: url,
Condition: {
DateLessThan: {
"AWS:EpochTime": Math.round(new Date(dateString).getTime() / 1000),
},
DateGreaterThan: {
"AWS:EpochTime": Math.round(new Date(dateGreaterThanString).getTime() / 1000),
},
},
},
],
});

const parsedUrl = parseUrl(result);
expect(parsedUrl).toBeDefined();
const signatureQueryParam = denormalizeBase64(parsedUrl.query!["Signature"] as string);
expect(verifySignature(signatureQueryParam, policyStr)).toBeTruthy();
});

it("allows number input for date range", () => {
const result = getSignedUrl({
url,
keyPairId,
dateLessThan: dateNumber as unknown as string,
dateGreaterThan: dateGreaterThanNumber as unknown as string,
privateKey,
passphrase,
});

const policyStr = JSON.stringify({
Statement: [
{
Resource: url,
Condition: {
DateLessThan: {
"AWS:EpochTime": Math.round(dateNumber / 1000),
},
DateGreaterThan: {
"AWS:EpochTime": Math.round(dateGreaterThanNumber / 1000),
},
},
},
],
});

const parsedUrl = parseUrl(result);
expect(parsedUrl).toBeDefined();
const signatureQueryParam = denormalizeBase64(parsedUrl.query!["Signature"] as string);
expect(verifySignature(signatureQueryParam, policyStr)).toBeTruthy();
});
it("allows Date object input for date range", () => {
const result = getSignedUrl({
url,
keyPairId,
dateLessThan: dateObject as unknown as string,
dateGreaterThan: dateGreaterThanObject as unknown as string,
privateKey,
passphrase,
});

const policyStr = JSON.stringify({
Statement: [
{
Resource: url,
Condition: {
DateLessThan: {
"AWS:EpochTime": Math.round(dateObject.getTime() / 1000),
},
DateGreaterThan: {
"AWS:EpochTime": Math.round(dateGreaterThanObject.getTime() / 1000),
},
},
},
],
});

const parsedUrl = parseUrl(result);
expect(parsedUrl).toBeDefined();
const signatureQueryParam = denormalizeBase64(parsedUrl.query!["Signature"] as string);
expect(verifySignature(signatureQueryParam, policyStr)).toBeTruthy();
});
});
6 changes: 3 additions & 3 deletions packages/cloudfront-signer/src/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,12 +359,12 @@ class CloudfrontSignBuilder {
return Math.round(date.getTime() / 1000);
}

private parseDate(date?: string): number | undefined {
private parseDate(date?: string | number | Date): number | undefined {
if (!date) {
return undefined;
}
const parsedDate = Date.parse(date);
return isNaN(parsedDate) ? undefined : this.epochTime(new Date(parsedDate));
const parsedDate = new Date(date);
return isNaN(parsedDate.getTime()) ? undefined : this.epochTime(parsedDate);
}

private parseDateWindow(expiration: string, start?: string): PolicyDates {
Expand Down
Loading