data "aws_s3_bucket" "waf_logging_bucket" {
  bucket = "aws-waf-logs-prod-tf-orcd-bucket"
}

resource "aws_wafv2_ip_set" "smf_api_ip_set" {
  name               = "${var.environment}-${var.service_name}-ip-set"
  description        = "IP Set for SMF Fan Response API Gateway filtering"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = var.allowed_smf_ip_addresses
}

resource "aws_wafv2_ip_set" "tunespeak_api_ip_set" {
  name               = "${local.tunespeak_prefix}-ip-set"
  description        = "IP Set for Tunespeak Fan Response API Gateway filtering"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = var.allowed_tunespeak_ip_addresses
}

resource "aws_wafv2_ip_set" "wyng_api_ip_set" {
  name               = "${local.wyng_prefix}-ip-set"
  description        = "IP Set for Wyng Fan Response API Gateway filtering"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = var.allowed_wyng_ip_addresses
}

resource "aws_wafv2_ip_set" "mtl_api_ip_set" {
  name               = "${local.mtl_prefix}-ip-set"
  description        = "IP Set for MTL Fan Response API Gateway filtering"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = var.allowed_mtl_ip_addresses
}

data "aws_wafv2_ip_set" "nat" {
  name  = "nat"
  scope = "REGIONAL"
}

resource "aws_wafv2_rule_group" "fan_response_api_rule_group" {
  name        = "${local.prefix}-filtering-rule-group"
  description = "${var.environment}-${var.service_name} custom rule group"
  scope       = "REGIONAL"
  capacity    = 500

  rule {
    name     = "${local.prefix}-allow-rule"
    priority = 1

    action {
      allow {}
    }

    statement {
      or_statement {

        # Match SMF IP.
        statement {

          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.smf_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/${local.smf_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR Match tunespeak IP.
        statement {
          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.tunespeak_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/${local.tunespeak_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match wyng IP.
        statement {
          and_statement {
            statement {
              ip_set_reference_statement {
                arn = aws_wafv2_ip_set.wyng_api_ip_set.arn
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/${local.wyng_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match only SMF IP accessing /external/roster
        statement {
          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.smf_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "EXACTLY"
                search_string         = "/external/roster"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match MTL IP.
        statement {
          and_statement {
            statement {
              ip_set_reference_statement {
                arn = aws_wafv2_ip_set.mtl_api_ip_set.arn
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/${local.mtl_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match SMF IP accessing /external/smf/
        statement {
          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.smf_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/external/${local.smf_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match wyng IP accessing /external/wyng/
        statement {
          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.wyng_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/external/${local.wyng_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

        # OR match tunespeak IP accessing /external/tunespeak/
        statement {
          and_statement {
            statement {
              or_statement {
                statement {
                  ip_set_reference_statement {
                    arn = aws_wafv2_ip_set.tunespeak_api_ip_set.arn
                  }
                }
                statement {
                  ip_set_reference_statement {
                    arn = data.aws_wafv2_ip_set.nat.arn
                  }
                }
              }
            }

            statement {
              byte_match_statement {
                field_to_match {
                  uri_path {}
                }
                positional_constraint = "STARTS_WITH"
                search_string         = "/external/${local.tunespeak_name}/"
                text_transformation {
                  priority = 1
                  type     = "NONE"
                }
              }
            }
          }
        }

      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "${local.prefix}-allow-traffic"
      sampled_requests_enabled   = true
    }
  }

  # Block everything else that was not allowed by the allow rule.
  rule {
    name     = "${local.prefix}-block-rule"
    priority = 2

    action {
      block {}
    }

    statement {
      byte_match_statement {
        field_to_match {
          uri_path {}
        }
        positional_constraint = "STARTS_WITH" # Block all other requests
        search_string         = "/"
        text_transformation {
          priority = 1
          type     = "NONE"
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "${local.prefix}-block-traffic"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "${local.prefix}-rule-group"
    sampled_requests_enabled   = true
  }
}

module "fan_response_rest_api_waf" {
  source            = "git@github.com:theorchard/terraform-aws-waf.git//?ref=2.0.2"
  environment       = var.environment
  service_name      = var.service_name
  aws_region        = var.aws_region
  count_waf_enabled = false
  block_waf_enabled = true

  custom_rules = [
    {
      name                       = "${var.environment}-${var.service_name}-waf-custom-rules-group"
      rule_group_arn             = aws_wafv2_rule_group.fan_response_api_rule_group.arn
      priority                   = 3
      cloudwatch_metrics_enabled = true
      metric_name                = "waf-block-acl"
      sampled_requests_enabled   = true
    },
  ]
}

resource "aws_wafv2_web_acl_association" "smf_rest_api_gateway_waf_association" {
  resource_arn = aws_api_gateway_stage.sme_fan_response_rest_api_stage.arn
  web_acl_arn  = module.fan_response_rest_api_waf.waf_blocking_arn_output
}
