Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

feat(aws-cloudfront, nextjs-component): support setting geo restrictions #726

Merged
merged 2 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ myNextApplication:
responseCode: 500 # optional, alters the response code
comment: "a comment" # optional, describes your distribution
webACLId: "arn:aws:wafv2:us-east-1:123456789012:global/webacl/ExampleWebACL/473e64fd-f30b-4765-81a0-62ad96dd167a" # ARN of WAF
restrictions:
geoRestriction:
restrictionType: "blacklist" # valid values are whitelist/blacklist/none. Set to "none" and omit items to disable restrictions
items: ["AA"] # ISO 3166 alpha-2 country codes
```

This is particularly useful for caching any of your Next.js pages at CloudFront's edge locations. See [this](https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/serverless-components/nextjs-component/examples/app-with-custom-caching-config) for an example application with custom cache configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,117 @@ describe("General options propagation", () => {
})
);
});

it("create distribution with restrictions and updates it", async () => {
// Create
await component.default({
restrictions: {
geoRestriction: {
restrictionType: "blacklist",
items: ["AA"]
}
},
origins
});

expect(mockCreateDistribution).toBeCalledWith(
expect.objectContaining({
DistributionConfig: expect.objectContaining({
Restrictions: {
GeoRestriction: {
RestrictionType: "blacklist",
Quantity: 1,
Items: ["AA"]
}
}
})
})
);

// Update
await component.default({
restrictions: {
geoRestriction: {
restrictionType: "blacklist",
items: ["ZZ"]
}
},
origins
});

expect(mockUpdateDistribution).toBeCalledWith(
expect.objectContaining({
DistributionConfig: expect.objectContaining({
Restrictions: {
GeoRestriction: {
RestrictionType: "blacklist",
Quantity: 1,
Items: ["ZZ"]
}
}
})
})
);
});

it("create distribution with restrictions and deletes it", async () => {
// Create
await component.default({
restrictions: {
geoRestriction: {
restrictionType: "blacklist",
items: ["AA"]
}
},
origins
});

expect(mockCreateDistribution).toBeCalledWith(
expect.objectContaining({
DistributionConfig: expect.objectContaining({
Restrictions: {
GeoRestriction: {
RestrictionType: "blacklist",
Quantity: 1,
Items: ["AA"]
}
}
})
})
);

// Delete
await component.default({
restrictions: {
geoRestriction: {
restrictionType: "none"
}
},
origins
});

expect(mockUpdateDistribution).toBeCalledWith(
expect.objectContaining({
DistributionConfig: expect.objectContaining({
Restrictions: {
GeoRestriction: {
RestrictionType: "none",
Quantity: 0
}
}
})
})
);

// Restriction items not needed when deleting it
expect.objectContaining({
DistributionConfig: expect.not.objectContaining({
Restrictions: {
GeoRestriction: {
Items: expect.anything()
}
}
})
});
});
});
35 changes: 35 additions & 0 deletions packages/serverless-components/aws-cloudfront/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ const createCloudFrontDistribution = async (cf, s3, inputs) => {
distributionConfig.WebACLId = inputs.webACLId;
}

// Set restrictions
if (inputs.restrictions !== undefined && inputs.restrictions !== null) {
const geoRestriction = inputs.restrictions.geoRestriction;

distributionConfig.Restrictions = {
GeoRestriction: {
RestrictionType: geoRestriction.restrictionType,
Quantity: geoRestriction.items ? geoRestriction.items.length : 0
}
};

if (geoRestriction.items && geoRestriction.items.length > 0) {
distributionConfig.Restrictions.GeoRestriction.Items =
geoRestriction.items;
}
}

const res = await cf.createDistribution(params).promise();

return {
Expand Down Expand Up @@ -146,6 +163,24 @@ const updateCloudFrontDistribution = async (cf, s3, distributionId, inputs) => {
params.DistributionConfig.WebACLId = inputs.webACLId;
}

// When updating, don't override any existing geo restrictions if not set in inputs
if (inputs.restrictions !== undefined && inputs.restrictions !== null) {
const geoRestriction = inputs.restrictions.geoRestriction;

params.DistributionConfig.Restrictions = {
GeoRestriction: {
RestrictionType: geoRestriction.restrictionType,
Quantity: geoRestriction.items ? geoRestriction.items.length : 0,
Items: geoRestriction.items
}
};

if (geoRestriction.items && geoRestriction.items.length > 0) {
params.DistributionConfig.Restrictions.GeoRestriction.Items =
geoRestriction.items;
}
}

let s3CanonicalUserId;
let originAccessIdentityId;

Expand Down
3 changes: 2 additions & 1 deletion packages/serverless-components/aws-cloudfront/serverless.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class CloudFront extends Component {
!equals(this.state.aliases, inputs.aliases) ||
!equals(this.state.priceClass, inputs.priceClass) ||
!equals(this.state.errorPages, inputs.errorPages) ||
!equals(this.state.webACLId, inputs.webACLId)
!equals(this.state.webACLId, inputs.webACLId) ||
!equals(this.state.restrictions, inputs.restrictions)
) {
this.context.debug(
`Updating CloudFront distribution of ID ${this.state.id}.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1084,5 +1084,18 @@ describe("Custom inputs", () => {
}
});
});

it("sets restrictions", async () => {
await createNextComponent().default({
cloudfront: {
restrictions: {
geoRestriction: {
restrictionType: "blacklist",
items: ["AA"]
}
}
}
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class NextjsComponent extends Component {
distributionId: cloudFrontDistributionId = null,
comment: cloudFrontComment,
webACLId: cloudFrontWebACLId,
restrictions: cloudFrontRestrictions,
...cloudFrontOtherInputs
} = inputs.cloudfront || {};

Expand Down Expand Up @@ -589,7 +590,8 @@ class NextjsComponent extends Component {
errorPages: cloudFrontErrorPagesInputs
}),
comment: cloudFrontComment,
webACLId: cloudFrontWebACLId
webACLId: cloudFrontWebACLId,
restrictions: cloudFrontRestrictions
});

let appUrl = cloudFrontOutputs.url;
Expand Down