# GetObjectAcl

GetObjectAcl returns an object's access control list (ACL) - the list of who may do what with
the object. On STRG, objects share their bucket's ACL rather than carrying one of their own,
so every object answers with its bucket's: `private` when the bucket is *Protected*, and
`public-read` when its *Bucket Protection* is off. That keeps things simple - one setting per
bucket decides who can read its objects. The same answer, asked of the bucket itself, comes from
[GetBucketAcl](https://docs.strg.com/s3-api/GetBucketAcl).

**Info: Good to know on STRG**

- The answer is the bucket's ACL, so it is always `private` or `public-read`, with no
  per-object grants to account for.
- The owner, and the owner's grant, name your STRG account.
- `x-amz-expected-bucket-owner` is welcome and has nothing to check: a key only ever reaches
  buckets of its own account.

## When you'd use it

- **Checking a link before sharing it.** An app or an agent about to hand someone a plain URL to
  an object can first confirm the object is `public-read`, so the link opens for whoever
  receives it.
- **Tools written for Amazon S3.** Backup tools, sync clients and file browsers that read an
  object's ACL to show or copy its permissions get an answer in the shape they expect.

## Request

```http
GET /photos/2024/cat.jpg?acl HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

The path-style form is `GET /my-bucket/photos/2024/cat.jpg?acl` on `s3.eu-central-1.strg.com` -
[Addressing](https://docs.strg.com/s3-api/overview#addressing) explains both.

### Permissions

The access key needs **Read objects** (`objects:read`). For a key limited to certain buckets or
to a key prefix, the bucket and the object key are within them; anything else answers
`403 AccessDenied`. [Access keys](https://docs.strg.com/access-keys) has the details.

### URI parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Bucket` | String | Yes | The bucket that holds the object. |
| `Key` | String | Yes | The object's key. A key containing a `.` or `..` path segment (`a/../b`) cannot be put in a URL and answers `400 InvalidURI`; its ACL is its bucket's, which [GetBucketAcl](https://docs.strg.com/s3-api/GetBucketAcl) reads. |
| `acl` | Flag | Yes | Selects the ACL subresource. Sent with no value. |
| `versionId` | String | No | A specific version of the object. Without it, the current version is used. |

### Request headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `x-amz-expected-bucket-owner` | String | No | Welcome, with nothing to check: an access key only ever reaches its own account's buckets. |
| `x-amz-request-payer` | String | No | Has no effect: STRG has no Requester Pays buckets, so there is never a need to send it. |

### Request body

None.

## Response

### Response headers

| Name | Description |
| --- | --- |
| `Content-Type` | `application/xml` |
| `x-amz-request-id` | The request ID, handy for a support request. |

`x-amz-request-charged` is never returned, since STRG has no Requester Pays.

### Response body

The answer is an `AccessControlPolicy` document. Its elements are:

| Element | Description |
| --- | --- |
| `Owner` | The owner. `ID` and `DisplayName` both carry your STRG account ID. |
| `AccessControlList` | The grants (`Grants` in the SDKs). Always one `Grant` giving the owner `FULL_CONTROL`, plus - when the bucket's *Bucket Protection* is off - one giving the `AllUsers` group `READ`. |
| `Grant` / `Grantee` | For the owner, a `CanonicalUser` grantee whose `ID` and `DisplayName` are your account ID. For public read, a `Group` grantee with `URI` `http://acs.amazonaws.com/groups/global/AllUsers`. |
| `Grant` / `Permission` | `FULL_CONTROL` for the owner, `READ` for `AllUsers`. |

Here is the answer for an object in a *Protected* (`private`) bucket:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Owner>
    <ID>your-account-id</ID>
    <DisplayName>your-account-id</DisplayName>
  </Owner>
  <AccessControlList>
    <Grant>
      <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
        <ID>your-account-id</ID>
        <DisplayName>your-account-id</DisplayName>
      </Grantee>
      <Permission>FULL_CONTROL</Permission>
    </Grant>
  </AccessControlList>
</AccessControlPolicy>
```

An object in a bucket whose *Bucket Protection* is off answers the same document with a second
`Grant`, to the `AllUsers` group, for `READ`.

**Note: What `public-read` means**

With `public-read`, anyone can read the object without signing - a [GetObject](https://docs.strg.com/s3-api/GetObject)
or [HeadObject](https://docs.strg.com/s3-api/HeadObject) on the bucket's regional endpoint. Reading its ACL,
GetObjectAcl included, still takes a signed request.
[Anonymous reads](https://docs.strg.com/s3-api/overview#anonymous-reads) has the details.

## Errors

These are the errors this request can return, and the situation behind each one.

| Code | HTTP status | When |
| --- | --- | --- |
| `NoSuchKey` | 404 | The object does not exist. |
| `NoSuchBucket` | 404 | The bucket does not exist, it is in a region other than the endpoint's, or the account has no storage in this endpoint's region yet. |
| `AccessDenied` | 403 | The access key lacks *Read objects*, the bucket or key is outside its buckets or prefix, the account is not active, or the request is unsigned. |
| `InvalidAccessKeyId` | 403 | The access key does not exist, has expired or been revoked - or it was created moments ago and is not usable yet in this region where the account already stores data, in which case the same request works shortly afterwards. |
| `InvalidURI` | 400 | The key contains a `.` or `..` path segment. |
| `SignatureDoesNotMatch` | 403 | The request signature does not match. |
| `RequestTimeTooSkewed` | 403 | The request time is more than 15 minutes from the server's. |
| `AuthorizationHeaderMalformed` | 400 | The signature's credential scope names a region other than the endpoint's. |
| `NotImplemented` | 501 | The request carries a query parameter other than `acl` and `versionId` (presigning's `X-Amz-*` parameters aside). |
| `SlowDown` | 503 | Too many requests. The `Retry-After` header says when a retry is welcome; the AWS SDKs retry on their own. |
| `ServiceUnavailable` | 503 | A temporary failure; the same request can be retried. |
| `InternalError` | 500 | An unexpected failure; the same request can be retried. |

## Differences from Amazon S3

The table lists each place where STRG behaves differently, and what that means for a client.

| Aspect | Amazon S3 | STRG |
| --- | --- | --- |
| Object ACLs | Each object has its own ACL | An object's ACL is always its bucket's - one setting per bucket. [PutObjectAcl](https://docs.strg.com/s3-api/PutObjectAcl) succeeds when it matches |
| Grants | Any combination of grants to users, groups and email addresses | Owner `FULL_CONTROL`, plus `AllUsers` `READ` for a bucket whose *Bucket Protection* is off - two cases, nothing more to interpret |
| Owner | Your AWS canonical user ID | Your STRG account ID, in both `ID` and `DisplayName` |
| Anonymous requests | Allowed when `READ_ACP` is granted to everyone | Always signed: an unsigned request answers `403 AccessDenied` |
| Bucket in another region | `301` redirect with `x-amz-bucket-region` | `404 NoSuchBucket`. A bucket is reached through its own regional endpoint |
| Keys with `.` or `..` segments | Allowed | `400 InvalidURI`. Such a key's ACL is its bucket's, which [GetBucketAcl](https://docs.strg.com/s3-api/GetBucketAcl) reads |
| `x-amz-expected-bucket-owner` | Checked against the bucket owner | Welcome, with nothing to check |

## Examples

Reading the ACL of `photos/2024/cat.jpg` in `my-bucket`, and telling whether it is public:

**AWS CLI**

```bash
aws s3api get-object-acl \
  --bucket my-bucket \
  --key photos/2024/cat.jpg \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1
```

**Python (boto3)**

```python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

acl = s3.get_object_acl(Bucket="my-bucket", Key="photos/2024/cat.jpg")
public = any(
    grant["Grantee"].get("URI") == "http://acs.amazonaws.com/groups/global/AllUsers"
    and grant["Permission"] == "READ"
    for grant in acl["Grants"]
)
print("public-read" if public else "private")
```

**JavaScript (AWS SDK v3)**

```javascript
import { GetObjectAclCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

const { Grants = [] } = await client.send(
  new GetObjectAclCommand({ Bucket: "my-bucket", Key: "photos/2024/cat.jpg" }),
);
const isPublic = Grants.some(
  (grant) =>
    grant.Grantee?.URI === "http://acs.amazonaws.com/groups/global/AllUsers" &&
    grant.Permission === "READ",
);
console.log(isPublic ? "public-read" : "private");
```

A specific version is read by adding `--version-id` (CLI), `VersionId=` (boto3) or `VersionId`
(JavaScript) with the version's ID.

## Related

- [PutObjectAcl](https://docs.strg.com/s3-api/PutObjectAcl)
- [GetBucketAcl](https://docs.strg.com/s3-api/GetBucketAcl)
- [PutBucketAcl](https://docs.strg.com/s3-api/PutBucketAcl)
- [GetObject](https://docs.strg.com/s3-api/GetObject)
- [Access keys](https://docs.strg.com/access-keys)
- [S3 API overview](https://docs.strg.com/s3-api/overview) - including [anonymous reads](https://docs.strg.com/s3-api/overview#anonymous-reads)
