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

Commit 964b936

Browse files
authored
feat(aws-cloudfront, nextjs-component): support setting geo restrictions (#726)
1 parent 9296274 commit 964b936

File tree

6 files changed

+170
-2
lines changed

6 files changed

+170
-2
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ myNextApplication:
213213
responseCode: 500 # optional, alters the response code
214214
comment: "a comment" # optional, describes your distribution
215215
webACLId: "arn:aws:wafv2:us-east-1:123456789012:global/webacl/ExampleWebACL/473e64fd-f30b-4765-81a0-62ad96dd167a" # ARN of WAF
216+
restrictions:
217+
geoRestriction:
218+
restrictionType: "blacklist" # valid values are whitelist/blacklist/none. Set to "none" and omit items to disable restrictions
219+
items: ["AA"] # ISO 3166 alpha-2 country codes
216220
```
217221

218222
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.

packages/serverless-components/aws-cloudfront/__tests__/general-options.test.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,117 @@ describe("General options propagation", () => {
243243
})
244244
);
245245
});
246+
247+
it("create distribution with restrictions and updates it", async () => {
248+
// Create
249+
await component.default({
250+
restrictions: {
251+
geoRestriction: {
252+
restrictionType: "blacklist",
253+
items: ["AA"]
254+
}
255+
},
256+
origins
257+
});
258+
259+
expect(mockCreateDistribution).toBeCalledWith(
260+
expect.objectContaining({
261+
DistributionConfig: expect.objectContaining({
262+
Restrictions: {
263+
GeoRestriction: {
264+
RestrictionType: "blacklist",
265+
Quantity: 1,
266+
Items: ["AA"]
267+
}
268+
}
269+
})
270+
})
271+
);
272+
273+
// Update
274+
await component.default({
275+
restrictions: {
276+
geoRestriction: {
277+
restrictionType: "blacklist",
278+
items: ["ZZ"]
279+
}
280+
},
281+
origins
282+
});
283+
284+
expect(mockUpdateDistribution).toBeCalledWith(
285+
expect.objectContaining({
286+
DistributionConfig: expect.objectContaining({
287+
Restrictions: {
288+
GeoRestriction: {
289+
RestrictionType: "blacklist",
290+
Quantity: 1,
291+
Items: ["ZZ"]
292+
}
293+
}
294+
})
295+
})
296+
);
297+
});
298+
299+
it("create distribution with restrictions and deletes it", async () => {
300+
// Create
301+
await component.default({
302+
restrictions: {
303+
geoRestriction: {
304+
restrictionType: "blacklist",
305+
items: ["AA"]
306+
}
307+
},
308+
origins
309+
});
310+
311+
expect(mockCreateDistribution).toBeCalledWith(
312+
expect.objectContaining({
313+
DistributionConfig: expect.objectContaining({
314+
Restrictions: {
315+
GeoRestriction: {
316+
RestrictionType: "blacklist",
317+
Quantity: 1,
318+
Items: ["AA"]
319+
}
320+
}
321+
})
322+
})
323+
);
324+
325+
// Delete
326+
await component.default({
327+
restrictions: {
328+
geoRestriction: {
329+
restrictionType: "none"
330+
}
331+
},
332+
origins
333+
});
334+
335+
expect(mockUpdateDistribution).toBeCalledWith(
336+
expect.objectContaining({
337+
DistributionConfig: expect.objectContaining({
338+
Restrictions: {
339+
GeoRestriction: {
340+
RestrictionType: "none",
341+
Quantity: 0
342+
}
343+
}
344+
})
345+
})
346+
);
347+
348+
// Restriction items not needed when deleting it
349+
expect.objectContaining({
350+
DistributionConfig: expect.not.objectContaining({
351+
Restrictions: {
352+
GeoRestriction: {
353+
Items: expect.anything()
354+
}
355+
}
356+
})
357+
});
358+
});
246359
});

packages/serverless-components/aws-cloudfront/lib/index.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ const createCloudFrontDistribution = async (cf, s3, inputs) => {
9696
distributionConfig.WebACLId = inputs.webACLId;
9797
}
9898

99+
// Set restrictions
100+
if (inputs.restrictions !== undefined && inputs.restrictions !== null) {
101+
const geoRestriction = inputs.restrictions.geoRestriction;
102+
103+
distributionConfig.Restrictions = {
104+
GeoRestriction: {
105+
RestrictionType: geoRestriction.restrictionType,
106+
Quantity: geoRestriction.items ? geoRestriction.items.length : 0
107+
}
108+
};
109+
110+
if (geoRestriction.items && geoRestriction.items.length > 0) {
111+
distributionConfig.Restrictions.GeoRestriction.Items =
112+
geoRestriction.items;
113+
}
114+
}
115+
99116
const res = await cf.createDistribution(params).promise();
100117

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

166+
// When updating, don't override any existing geo restrictions if not set in inputs
167+
if (inputs.restrictions !== undefined && inputs.restrictions !== null) {
168+
const geoRestriction = inputs.restrictions.geoRestriction;
169+
170+
params.DistributionConfig.Restrictions = {
171+
GeoRestriction: {
172+
RestrictionType: geoRestriction.restrictionType,
173+
Quantity: geoRestriction.items ? geoRestriction.items.length : 0,
174+
Items: geoRestriction.items
175+
}
176+
};
177+
178+
if (geoRestriction.items && geoRestriction.items.length > 0) {
179+
params.DistributionConfig.Restrictions.GeoRestriction.Items =
180+
geoRestriction.items;
181+
}
182+
}
183+
149184
let s3CanonicalUserId;
150185
let originAccessIdentityId;
151186

packages/serverless-components/aws-cloudfront/serverless.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class CloudFront extends Component {
6060
!equals(this.state.aliases, inputs.aliases) ||
6161
!equals(this.state.priceClass, inputs.priceClass) ||
6262
!equals(this.state.errorPages, inputs.errorPages) ||
63-
!equals(this.state.webACLId, inputs.webACLId)
63+
!equals(this.state.webACLId, inputs.webACLId) ||
64+
!equals(this.state.restrictions, inputs.restrictions)
6465
) {
6566
this.context.debug(
6667
`Updating CloudFront distribution of ID ${this.state.id}.`

packages/serverless-components/nextjs-component/__tests__/custom-inputs.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,5 +1084,18 @@ describe("Custom inputs", () => {
10841084
}
10851085
});
10861086
});
1087+
1088+
it("sets restrictions", async () => {
1089+
await createNextComponent().default({
1090+
cloudfront: {
1091+
restrictions: {
1092+
geoRestriction: {
1093+
restrictionType: "blacklist",
1094+
items: ["AA"]
1095+
}
1096+
}
1097+
}
1098+
});
1099+
});
10871100
});
10881101
});

packages/serverless-components/nextjs-component/src/component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ class NextjsComponent extends Component {
240240
distributionId: cloudFrontDistributionId = null,
241241
comment: cloudFrontComment,
242242
webACLId: cloudFrontWebACLId,
243+
restrictions: cloudFrontRestrictions,
243244
...cloudFrontOtherInputs
244245
} = inputs.cloudfront || {};
245246

@@ -589,7 +590,8 @@ class NextjsComponent extends Component {
589590
errorPages: cloudFrontErrorPagesInputs
590591
}),
591592
comment: cloudFrontComment,
592-
webACLId: cloudFrontWebACLId
593+
webACLId: cloudFrontWebACLId,
594+
restrictions: cloudFrontRestrictions
593595
});
594596

595597
let appUrl = cloudFrontOutputs.url;

0 commit comments

Comments
 (0)