S3 has three separate mechanisms:

MechanismDescriptionExample
Bucket policiesJSON policies attached to buckets”allow public read from this bucket”
IAM/Identity policiesPolicies attached to users/roles”user Alice can read bucket X”
Object ACLsPer-object access control lists”this specific object is readable by user Bob”

Bucket policy

Here’s what one looks like:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789012:user/alice",
          "arn:aws:iam::123456789012:user/bob",
          "arn:aws:iam::123456789012:role/data-processor"
        ]
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/documents/*",
      "Condition": {
        "IpAddress": { "aws:SourceIp": "192.0.2.0/24" }
      }
    }
  ]
}

Identity policy

This is syntactically like a Bucket policy but it has no Principal field because it is bound to a principal.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/documents/*",
      "Condition": {
        "IpAddress": {"aws:SourceIp": "192.0.2.0/24"}
      }
    }
  ]
}

Object ACL

These things are ugly and deprecated.

<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy>
  <Owner>
    <ID>79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be</ID>
    <DisplayName>bucket-owner</DisplayName>
  </Owner>
  <AccessControlList>
    <Grant>
      <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xsi:type="CanonicalUser">
        <ID>79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be</ID>
        <DisplayName>bucket-owner</DisplayName>
      </Grantee>
      <Permission>FULL_CONTROL</Permission>
    </Grant>
    <Grant>
      <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xsi:type="CanonicalUser">
        <ID>a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2</ID>
        <DisplayName>alice</DisplayName>
      </Grantee>
      <Permission>READ</Permission>
    </Grant>
  </AccessControlList>
</AccessControlPolicy>

The ID tag (a1b2c3d4e... for alice) is a canonical user ID which is equivalent to arn:aws:iam::123456789012:user/alice. You can use these canonical user IDs in bucket policies if you want; that would look like "Principal": { "CanonicalUser": "a1b2c3d4e....