---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Serve static web content from private S3 bucket through HTTPS with HTTP Basic Auth. CloudFront distribution used to proxy private S3 content and mandate HTTPS-only traffic. CloudFront Functions used to implement HTTP Basic Auth with static username and password"
Parameters:
  Base64UserPass:
    Type: String
    NoEcho: true
Resources:
  SiteBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      AccessControl: Private
  SiteBucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref SiteBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "cloudfront.amazonaws.com"
            Action: 's3:GetObject'
            Resource: !Sub "${SiteBucket.Arn}/*"
            # CF Distribution ARNs have to be manually constructed
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${SiteDistribution}"
  SiteDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: !GetAtt SiteBucket.RegionalDomainName
            Id: SiteBucketWebsite          
            # This is necessary even thou we don't use OAI for access control.
            S3OriginConfig:
              OriginAccessIdentity: ""
            OriginAccessControlId: !GetAtt SiteOriginAccessControl.Id
        Enabled: true
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
          TargetOriginId: SiteBucketWebsite
          AllowedMethods:
            - HEAD
            - GET
            - OPTIONS
          Compress: true
          ViewerProtocolPolicy: redirect-to-https
          FunctionAssociations:
            - EventType: viewer-request
              FunctionARN: !GetAtt SiteAuthFunction.FunctionMetadata.FunctionARN
        PriceClass: PriceClass_100    
  SiteOriginAccessControl:
    Type: "AWS::CloudFront::OriginAccessControl"
    Properties:
      OriginAccessControlConfig:
        Name: "Static Site Origin Access Control"
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
  SiteAuthFunction:
    Type: AWS::CloudFront::Function
    Properties:
      AutoPublish: true
      FunctionCode: !Sub "
        function handler(event) {
          var authHeaders = event.request.headers.authorization;
          var expected = 'Basic ${Base64UserPass}';

          if (authHeaders && authHeaders.value === expected) {
            return event.request;
          }

          var response = {
            statusCode: 401,
            statusDescription: 'Unauthorized',
            headers: {
              'www-authenticate': {
                value: 'Basic realm=\"Enter credentials for this super secure site\"',
              },
            },
          };

          return response;
        }"
      FunctionConfig:
        Comment: Add HTTP Basic authentication to CloudFront
        Runtime: cloudfront-js-1.0
      Name: BasicAuthFn
Outputs:
  StaticWebsiteHostname:
    Value: !GetAtt SiteDistribution.DomainName
