data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "resource_based_access_policy" {

  # Cloudsearch
  dynamic "statement" {
    for_each = var.cloudsearch_domains_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "cloudsearch:BuildSuggesters",
        "cloudsearch:Define*",
        "cloudsearch:Describe*",
        "cloudsearch:document",
        "cloudsearch:IndexDocuments",
        "cloudsearch:List*",
        "cloudsearch:search",
        "cloudsearch:suggest",
        "cloudsearch:UpdateScalingParameters",
      ]

      resources = [for domain in var.cloudsearch_domains_or_prefixes_to_access : format("arn:aws:cloudsearch:*:${data.aws_caller_identity.current.account_id}:domain/%s", domain)]
    }
  }

  # DynamoDB
  dynamic "statement" {
    for_each = var.dynamodb_tables_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "dynamodb:*Item",
        "dynamodb:Batch*Item",
        "dynamodb:ConditionCheckItem",
        "dynamodb:CreateBackup",
        "dynamodb:Describe*",
        "dynamodb:CreateBackup",
        "dynamodb:ExportTableToPointInTime",
        "dynamodb:Get*",
        "dynamodb:List*",
        "dynamodb:PartiQL*",
        "dynamodb:Query",
        "dynamodb:RestoreTableToPointInTime",
        "dynamodb:Scan",
      ]

      resources = concat(
        # Format ARNs for tables, child resources (streams, indexes, etc), and global tables
        [for table in var.dynamodb_tables_or_prefixes_to_access : format("arn:aws:dynamodb:*:${data.aws_caller_identity.current.account_id}:table/%s", table)],
        [for table in var.dynamodb_tables_or_prefixes_to_access : format("arn:aws:dynamodb:*:${data.aws_caller_identity.current.account_id}:table/%s/*", table)],
        [for table in var.dynamodb_tables_or_prefixes_to_access : format("arn:aws:dynamodb:*:${data.aws_caller_identity.current.account_id}:global-table/%s", table)]
      )
    }
  }

  # Elastic Transcoder
  dynamic "statement" {
    for_each = var.elastic_transcoder_pipelines_or_presets_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "elastictranscoder:*Job",
        "elastictranscoder:List*",
        "elastictranscoder:Read*",
        "elastictranscoder:TestRole",
        "elastictranscoder:UpdatePipelineStatus",
      ]

      resources = concat(
        ["arn:aws:elastictranscoder:*:${data.aws_caller_identity.current.account_id}:job/*"],
        [for pipeline in var.elastic_transcoder_pipelines_or_presets_or_prefixes_to_access : format("arn:aws:elastictranscoder:*:${data.aws_caller_identity.current.account_id}:pipeline/%s", pipeline)],
        [for preset in var.elastic_transcoder_pipelines_or_presets_or_prefixes_to_access : format("arn:aws:elastictranscoder:*:${data.aws_caller_identity.current.account_id}:preset/%s", preset)],
      )
    }
  }

  # Kinesis
  dynamic "statement" {
    for_each = var.kinesis_streams_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "kinesis:*StreamRetentionPeriod",
        "kinesis:*EnhancedMonitoring",
        "kinesis:Describe*",
        "kinesis:Get*",
        "kinesis:List*",
        "kinesis:MergeShards",
        "kinesis:PutRecord",
        "kinesis:PutRecords",
        "kinesis:SplitShard",
        "kinesis:UpdateShardCount",
      ]

      resources = [for stream in var.kinesis_streams_or_prefixes_to_access : format("arn:aws:kinesis:*:${data.aws_caller_identity.current.account_id}:stream/%s", stream)]
    }
  }

  # Lambda
  dynamic "statement" {
    for_each = var.lambda_functions_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "lambda:Get*",
        "lambda:Invoke*",
        "lambda:List*",
      ]

      resources = concat(
        [for function in var.lambda_functions_or_prefixes_to_access : format("arn:aws:lambda:*:${data.aws_caller_identity.current.account_id}:function:%s", function)],
        [for function in var.lambda_functions_or_prefixes_to_access : format("arn:aws:lambda:*:${data.aws_caller_identity.current.account_id}:function:%s:*", function)],
      )
    }
  }

  # MediaConvert
  dynamic "statement" {
    for_each = length(concat(var.mediaconvert_jobs_or_prefixes_to_access, var.mediaconvert_queues_or_prefixes_to_access, var.mediaconvert_jobtemplates_or_prefixes_to_access, var.mediaconvert_presets_or_prefixes_to_access)) != 0 ? [1] : []
    content {
      actions = [
        "mediaconvert:CancelJob",
        "mediaconvert:Create*",
        "mediaconvert:Describe*",
        "mediaconvert:Get*",
        "mediaconvert:List*",
        "mediaconvert:*JobTemplate",
        "mediaconvert:UpdatePreset",
        "mediaconvert:UpdateQueue",
      ]

      resources = concat(
        [for job in coalesce(var.mediaconvert_jobs_or_prefixes_to_access, []) : format("arn:aws:mediaconvert:*:${data.aws_caller_identity.current.account_id}:jobs/%s", job)],
        [for queue in coalesce(var.mediaconvert_queues_or_prefixes_to_access, []) : format("arn:aws:mediaconvert:*:${data.aws_caller_identity.current.account_id}:queues/%s", queue)],
        [for template in coalesce(var.mediaconvert_jobtemplates_or_prefixes_to_access, []) : format("arn:aws:mediaconvert:*:${data.aws_caller_identity.current.account_id}:jobTemplates/%s", template)],
        [for preset in coalesce(var.mediaconvert_presets_or_prefixes_to_access, []) : format("arn:aws:mediaconvert:*:${data.aws_caller_identity.current.account_id}:presets/%s", preset)],
      )
    }
  }

  dynamic "statement" {
    for_each = var.mediaconvert_passed_roles_to_access != null ? [1] : []
    content {
      actions = [
        "iam:PassRole",
      ]

      resources = [for role in var.mediaconvert_passed_roles_to_access : format("arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/%s", role)]

      condition {
        test     = "StringEquals"
        variable = "iam:PassedToService"

        values = ["mediaconvert.amazonaws.com"]
      }
    }
  }

  # MWAA (Airflow)
  dynamic "statement" {
    for_each = var.managed_airflow_environments_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "airflow:CreateCliToken",
        "airflow:CreateWebLoginToken",
      ]

      resources = concat(
        [for environment in var.managed_airflow_environments_or_prefixes_to_access : format("arn:aws:airflow:*:${data.aws_caller_identity.current.account_id}:environment/%s", environment)],
        [for environment in var.managed_airflow_environments_or_prefixes_to_access : format("arn:aws:airflow:*:${data.aws_caller_identity.current.account_id}:role/%s/*", environment)],
      )
    }
  }

  # Step Functions
  dynamic "statement" {
    for_each = var.stepfunctions_state_machines_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "states:StopExecution",
      ]

      resources = [for state_machine in var.stepfunctions_state_machines_or_prefixes_to_access : format("arn:aws:states:*:${data.aws_caller_identity.current.account_id}:execution:%s:*", state_machine)]
    }
  }

  # SQS
  dynamic "statement" {
    for_each = var.sqs_queues_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "sqs:*Message",
        "sqs:ChangeMessageVisibility",
        "sqs:Get*",
        "sqs:List*",
        "sqs:PurgeQueue",
        "sqs:StartMessageMoveTask",
      ]

      resources = [for queue in var.sqs_queues_or_prefixes_to_access : format("arn:aws:sqs:*:${data.aws_caller_identity.current.account_id}:%s", queue)]
    }
  }

  # S3
  dynamic "statement" {
    for_each = var.s3_buckets_or_prefixes_to_access != null ? [1] : []
    content {
      actions = [
        "s3:*Object",
        "s3:*Object*",
        "s3:AbortMultipartUpload",
        "s3:Describe*",
        "s3:Get*",
        "s3:List*",
        "s3:Replicate*",
      ]

      resources = concat(
        # Some actions are on the naked bucket ARN, while most are on object prefixes
        [for bucket in var.s3_buckets_or_prefixes_to_access : format("arn:aws:s3:::%s", bucket)],
        [for bucket in var.s3_buckets_or_prefixes_to_access : format("arn:aws:s3:::%s/*", bucket)],
      )
    }
  }
}

data "aws_iam_policy_document" "noop_dummy_policy" {
  statement {
    actions = [
      "none:null",
    ]

    effect = "Allow"

    resources = ["*"]
  }
}

resource "aws_iam_policy" "resource_based_access_policy" {
  for_each = {
    1 = (
      # If no RBAC policy statement is included we want to set a no-op policy
      strcontains(data.aws_iam_policy_document.resource_based_access_policy.json, "Statement") ? 
      data.aws_iam_policy_document.resource_based_access_policy.json : 
      data.aws_iam_policy_document.noop_dummy_policy.json
    ),
  }
  name   = "RBAC-${var.application_family}-resource-access-policy-part-${each.key}"
  policy = each.value

  tags = {
    access_type = "rbac",
  }
}
