Skip to content

Commit

Permalink
Merge pull request #825 from aligent/feature/DO-1419_static_hosting_c…
Browse files Browse the repository at this point in the history
…ustom_header_policies

Static hosting's custom header policies.
This feature enable us to create and attach response header policies to specified path.
  • Loading branch information
krishanthisera authored Jan 4, 2023
2 parents 9e8debf + 1f2edbd commit aab93ce
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 45 additions & 1 deletion packages/static-hosting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

## Overview

This repository defines a CDK construct for hosting a static website on AWS using S3 and CloudFront.
This repository defines a CDK construct for hosting a static website on AWS using S3 and CloudFront.
It can be imported and used within CDK applications.

## Example

The following CDK snippet can be used to provision a static hosting stack using this construct.

```
Expand Down Expand Up @@ -43,3 +44,46 @@ new HostingStack(app, 'hosting-stack', {
});
```

### Response Header Policies

You can initialize [Response Headers Policies], map them and pass to the construct.

1. Create a policy

```sh
// Creating a custom response headers policy -- all parameters optional
const reportUriPolicy = new ResponseHeadersPolicy(this, 'ReportUriPolicy', {
responseHeadersPolicyName: 'ReportUriPolicy',
comment: 'To enable CSP Reporting',
customHeadersBehavior: {
customHeaders: [
{
header: 'content-security-policy-report-only',
value: `default-src 'none'; form-action 'none'; frame-ancestors 'none'; report-uri https://some-report-uri-domain.report-uri.com/r/t/csp/wizard`,
override: true
},
],
},
});
```

2. Attached policy to desired cache behavior or path

```sh
const responseHeaders: ResponseHeaderMappings[] = [{
header: reportUriPolicy,
pathPatterns: ['/au*', '/nz*']
attachToDefault: false
}];
```

If you should attached the policy to the Default Behavior, set `attachToDefault: true`

3. Include the config as props

```sh
new StaticHosting(this, 'pwa-stack', {...staticProps, ...{behaviors, customOriginConfigs, responseHeaders}});
```

[Response Headers Policies]:https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-cloudfront.ResponseHeadersPolicy.html
74 changes: 74 additions & 0 deletions packages/static-hosting/lib/static-hosting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,24 @@ export interface StaticHostingProps {
* Optional WAF ARN
*/
webAclArn?: string;

/**
* Response Header policies
*/
responseHeadersPolicies?: ResponseHeaderMappings[];
}

interface remapPath {
from: string,
to: string
}

export interface ResponseHeaderMappings {
header: ResponseHeadersPolicy,
pathPatterns: string[],
attachToDefault?: boolean
}

export class StaticHosting extends Construct {
private staticFiles = ["js", "css", "json", "svg", "jpg", "jpeg", "png"];

Expand Down Expand Up @@ -303,6 +314,69 @@ export class StaticHosting extends Construct {
});
}

/**
* Response Header policies
* This feature helps to attached custom ResponseHeadersPolicies to
* the cache behaviors
*/
if (props.responseHeadersPolicies) {
const cfnDistribution = distribution.node.defaultChild as CfnDistribution;

/**
* If we prepend custom origin configs,
* it would change the array indexes.
*/
let numberOfCustomBehaviors = 0;
if (props.prependCustomOriginBehaviours) {
numberOfCustomBehaviors = props.customOriginConfigs?.reduce((acc, current) => acc + current.behaviors.length, 0)!;
}

props.responseHeadersPolicies.forEach( (policyMapping) => {
/**
* If the policy should be attached to default behavior
*/
if (policyMapping.attachToDefault) {
cfnDistribution.addOverride(
`Properties.DistributionConfig.` +
`DefaultCacheBehavior.` +
`ResponseHeadersPolicyId`,
policyMapping.header.responseHeadersPolicyId
);
new CfnOutput(this, `response header policies ${policyMapping.header.node.id} default`, {
description: `response header policy mappings`,
value: `{ path: "default", policy: "${policyMapping.header.responseHeadersPolicyId}" }`,
exportName: `${exportPrefix}HeaderPolicy-default`
});
};
/**
* If the policy should be attached to
* specified path patterns
*/
policyMapping.pathPatterns.forEach(path => {
/**
* Looking for the index of the behavior
* according to the path pattern
* If the path patter is not found, it would be ignored
*/
let behaviorIndex = props.behaviors?.findIndex(behavior => {return behavior.pathPattern === path})! + numberOfCustomBehaviors;

if (behaviorIndex >= numberOfCustomBehaviors){
cfnDistribution.addOverride(
`Properties.DistributionConfig.CacheBehaviors.` +
`${behaviorIndex}` +
`.ResponseHeadersPolicyId`,
policyMapping.header.responseHeadersPolicyId
);
new CfnOutput(this, `response header policies ${policyMapping.header.node.id} ${path.replace(/\W/g, '')}`, {
description: `response header policy mappings`,
value: `{ path: "${path}", policy: "${policyMapping.header.responseHeadersPolicyId}"}`,
exportName: `${exportPrefix}HeaderPolicy-${path.replace(/\W/g, '')}`
});
};
});
});
}

if (publisherGroup) {
const cloudFrontInvalidationPolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
Expand Down
2 changes: 1 addition & 1 deletion packages/static-hosting/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aligent/cdk-static-hosting",
"version": "0.1.5",
"version": "0.1.13",
"main": "index.js",
"license": "GPL-3.0-only",
"homepage": "https://github.com/aligent/aws-cdk-static-hosting-stack#readme",
Expand Down

0 comments on commit aab93ce

Please sign in to comment.