@SneakyThrows
  public static String generatePolicy(Set<CredentialContext.Privilege> privileges, List<String> locations) {
    JsonNode policyRoot = loadYaml(POLICY_STATEMENT);
    ArrayNode policyStatement = (ArrayNode) policyRoot.findPath("Statement");
    JsonNode operationsStatement = loadYaml(OPERATION_STATEMENT);
    policyStatement.add(operationsStatement);

    // Add the appropriate S3 operations for the privileges requested
    ArrayNode actions = (ArrayNode) operationsStatement.findPath("Action");
    if (privileges.contains(CredentialContext.Privilege.UPDATE)) {
      UPDATE_ACTIONS.forEach(actions::add);
    } else if (privileges.contains(CredentialContext.Privilege.SELECT)) {
      SELECT_ACTIONS.forEach(actions::add);
    } else {
      throw new NotAuthorizedException(
        "Can't generate policy for unknown privileges '%s' for locations: '%s'"
          .formatted(privileges, locations));
    }

    // Group each location by s3 bucket it's located in, then for each
    // bucket, add the bucket arn for the listBucket and operations statements,
    // then add each path as a conditional prefix
    getBucketToPathsMap(locations).forEach(
      (bucketName, paths) -> {
        JsonNode listStatement = loadYaml(BUCKET_STATEMENT);
        policyStatement.add(listStatement);

        ArrayNode bucketResource = (ArrayNode) listStatement.findPath("Resource");
        ArrayNode operationsResource = (ArrayNode) operationsStatement.findPath("Resource");
        bucketResource.add("arn:aws:s3:::%s".formatted(bucketName));

        ArrayNode conditionalPrefixes = (ArrayNode) listStatement.findPath("s3:prefix");
        paths.forEach(
          path -> {
            // remove any preceding forward slashes
            // TODO: potentially sanitize/encode the whole path to deal with problematic chars
            String sanitizedPath = path.replaceAll("^/+", "");

            if (sanitizedPath.isEmpty()) {
              conditionalPrefixes.add("*");
              operationsResource.add("arn:aws:s3:::%s/*".formatted(bucketName));
            } else {
              conditionalPrefixes.add(sanitizedPath);
              conditionalPrefixes.add(sanitizedPath + "/");
              conditionalPrefixes.add(sanitizedPath + "/*");

              operationsResource.add("arn:aws:s3:::%s/%s/*".formatted(bucketName, sanitizedPath));
              operationsResource.add("arn:aws:s3:::%s/%s".formatted(bucketName, sanitizedPath));
            }
          });
      });

    return JSON_MAPPER.writeValueAsString(policyRoot);
  }
@SneakyThrows
  @Test
  public void testPolicySubstitution() {
    String bucket = "test-bucket";
    String prefix = "%s/%s".formatted(UUID.randomUUID(), UUID.randomUUID());
    String location = "s3://%s/%s".formatted(bucket, prefix);

    String policy = AwsPolicyGenerator.generatePolicy(Set.of(SELECT), List.of(location));

    JsonNode node = JSON_MAPPER.readTree(policy);
    assertThat(node.get("Statement").get(1).get("Resource").get(0).asText())
        .isEqualTo("arn:aws:s3:::" + bucket);

    node.findPath("s3:prefix").forEach(e -> assertThat(e.asText()).startsWith(prefix));
  }