/**
 * This sets up an S3 bucket for notifications assets (eg photos, documents). The bucket is
 * PUBLIC for read access and needs to be opened up to the API (which is deployed as an ECS task).
 *
 * It also needs to be opened up to worker (to create pre-signed urls for uploads)
 *
 * This bucket is:
 *  * public read access
 *  * private write access to the credentials (added onto the user rather than onto the bucket)
 */
#
# WARNING: this bucket uses IAM Policies and ACL for access
#
#  - @see https://binaryguy.tech/aws/s3/iam-policies-vs-s3-policies-vs-s3-bucket-acls/#:~:text=The%20biggest%20advantage%20of%20using,well%20as%20objects%20in%20it.
#  - @see https://aws.amazon.com/blogs/security/iam-policies-and-bucket-policies-and-acls-oh-my-controlling-access-to-s3-resources/
#  - @see https://stackoverflow.com/questions/47815526/s3-bucket-policy-vs-access-control-list
#
#
# TF: https://www.terraform.io/docs/providers/aws/r/s3_bucket.html
# AWS: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/s3api/create-bucket.html
resource "aws_s3_bucket" "notifications" {
  bucket = "${var.environment}-notifications-data"
  # removed for upgrade to aws provider 4.0
  # see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade#acl-argument
  # acl    = "private"
  tags = {
    Environment = var.environment
    Terraform   = true
  }
}
# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block
resource "aws_s3_bucket_public_access_block" "notifications" {
  bucket                  = aws_s3_bucket.notifications.id
  block_public_acls       = false
  # This feature protects your bucket from accidentally getting a policy that would enable public access
  # We WANT public access for read
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}
# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls
# this is required when setting the 'public-read' acl—without it it fails on apply
resource "aws_s3_bucket_ownership_controls" "notifications" {
  bucket = aws_s3_bucket.notifications.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}
# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl
resource "aws_s3_bucket_acl" "notifications" {
  bucket = aws_s3_bucket.notifications.id
  # Defaults to private
  # ["private" "public-read" "public-read-write" "authenticated-read" "aws-exec-read" "log-delivery-write"]
  acl    = "public-read"
  depends_on = [
    aws_s3_bucket_ownership_controls.notifications,
    aws_s3_bucket_public_access_block.notifications,
  ]
}
#
# Note: no point on having vesrioning enabled
#
#
# Restricted CORS policy but increased headers exposed (that could be further restrictred)
#
# TF: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_cors_configuration
# AWS: https://docs.amplify.aws/lib/storage/getting-started/q/platform/js/#amazon-s3-bucket-cors-policy-setup
resource "aws_s3_bucket_cors_configuration" "notifications" {
  bucket = aws_s3_bucket.notifications.id
  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = [
      "GET",
      "HEAD",
      "PUT",
    ]
    allowed_origins = ["/* OWN FQDN */"]
    expose_headers  = [
      "x-amz-server-side-encryption",
      "x-amz-request-id",
      "x-amz-id-2",
      "x-amz-meta-tag",
      "ETag"
    ]
    max_age_seconds = 3000
  }
}
resource "aws_s3_bucket_policy" "notifications" {
  bucket = aws_s3_bucket.notifications.id
  policy = data.aws_iam_policy_document.notifications-user.json
}
# [Data] IAM policy to define S3 permissions
#
# Restricting users files types to avoid problems (note: hand helds love to upload PDFs)
#
# TF: https://www.terraform.io/docs/providers/aws/d/iam_policy_document.html
# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html
# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html
data "aws_iam_policy_document" "notifications-user" {
  statement {
    sid    = "DenyNonImage"
    effect = "Deny"
    principals {
      identifiers = [aws_iam_user.notifications.arn]
      type        = "AWS"
    }
    actions = [
      # NOTE: this is an ACL permissions
      "s3:PutObjectAcl",
    ]
    not_resources = [
      "${aws_s3_bucket.notifications.arn}/*.png",
      "${aws_s3_bucket.notifications.arn}/*.jpg",
      "${aws_s3_bucket.notifications.arn}/*.jpeg",
      "${aws_s3_bucket.notifications.arn}/*.gif"
    ]
  }
  # NOTE: these rules exist on the user policy
/*
  statement {
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_iam_user.notifications.arn]
    }
    actions = [
      "s3:GetObject",
      "s3:DeleteObject",
      "s3:ListBucket",
      "s3:PutObject",
      # this is the magic spice, because the perms are created via ACL (rather than roles)
      "s3:PutObjectAcl"
    ]
    resources = [
      "${aws_s3_bucket.notifications.arn}*/
/*",
    ]
  }
*/
}
#######################
# TF: https://www.terraform.io/docs/providers/aws/r/iam_policy.html
# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html
# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html
resource "aws_iam_policy" "s3" {
  name        = "ecs-app-${var.environment}-s3"
  description = "Access from the ecs tasks to s3 buckets"
  policy      = data.aws_iam_policy_document.s3.json
}
# [Data] IAM policy to define S3 permissions
# TF: https://www.terraform.io/docs/providers/aws/d/iam_policy_document.html
# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html
# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/create-policy.html
data "aws_iam_policy_document" "s3" {
  statement {
    sid     = ""
    effect  = "Allow"
    actions = [
      "s3:DeleteObject",
      "s3:GetObject",
      "s3:PutObject",
      # this is the magic spice, because the perms are created via ACL (rather than roles)
      "s3:PutObjectAcl",
      "s3:ListBucket"
    ]
    resources = [
      "arn:aws:s3:::${var.assets_bucket}/*"
    ]
  }
}
######################
# Attaches a managed IAM policy to an IAM role for the ecs tasks
# TF: https://www.terraform.io/docs/providers/aws/r/iam_role_policy_attachment.html
# AWS: http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html
# AWS CLI: http://docs.aws.amazon.com/cli/latest/reference/iam/attach-role-policy.html
resource "aws_iam_role_policy_attachment" "s3" {
  role       = aws_iam_role.app_task.name  # need your role attached to whatever is doing the execution (eg workload task)
  policy_arn = aws_iam_policy.s3.arn
}