Repository: getmoto/moto
Issue category: Bug Report

Issue:
S3 get_object_attributes fails for GLACIER objects
Environment:

Python 3.11
Windows 11 Pro
moto==5.0.20
pytest==8.3.3

My failing test

   ```
 def test_get_glacier_object_attributes(self) -> None:
        s3 = client(
            "s3",
            region_name="eu-west-1"
        )
        s3.create_bucket(
            Bucket="test-bucket",
            CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
        )

        csv_body = "field_1,field_2\n1,2"

        s3.put_object(
            Body=csv_body.encode("UTF-8"),
            Bucket="test-bucket",
            Key="file.csv",
            StorageClass="GLACIER",
        )

        attributes = s3.get_object_attributes(
            Bucket="test-bucket",
            Key="file.csv",
            ObjectAttributes=["Checksum", "ObjectSize", "StorageClass"],
        )

        assert attributes is not None
```

The error 

```
....
if http.status_code >= 300:
            error_info = parsed_response.get("Error", {})
            error_code = error_info.get("QueryErrorCode") or error_info.get(
                "Code"
            )
            error_class = self.exceptions.from_code(error_code)
>           raise error_class(parsed_response, operation_name)
E           botocore.errorfactory.InvalidObjectState: An error occurred (InvalidObjectState) when calling the GetObjectAttributes operation: The operation is not valid for the object's storage class


..\..\.venv\Lib\site-packages\botocore\client.py:1009: InvalidObjectState
```


Expected behaviour is that the call to s3..get_object_attributes(
                    Bucket=bucket,
                    Key=key,
                    ObjectAttributes=["Checksum", "ObjectSize", "StorageClass"],
                )
returns a valid response as it does for non-glacier storage class object


Hints:

Patch:
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index eb79a3228ac4..d0c82428a953 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -1516,7 +1516,8 @@ def get_object(self) -> TYPE_RESPONSE:
         return 200, response_headers, key.value
 
     def get_object_attributes(self) -> TYPE_RESPONSE:
-        key, not_modified = self._get_key()
+        # Get the Key, but do not validate StorageClass - we can retrieve the attributes of Glacier-objects
+        key, not_modified = self._get_key(validate_storage_class=False)
         response_headers = self._get_cors_headers_other()
         if not_modified:
             return 304, response_headers, "Not Modified"
@@ -1608,7 +1609,7 @@ def list_parts(self) -> TYPE_RESPONSE:
             ),
         )
 
-    def _get_key(self) -> Tuple[FakeKey, bool]:
+    def _get_key(self, validate_storage_class: bool = True) -> Tuple[FakeKey, bool]:
         key_name = self.parse_key_name()
         version_id = self.querystring.get("versionId", [None])[0]
         if_modified_since = self.headers.get("If-Modified-Since")
@@ -1621,7 +1622,7 @@ def _get_key(self) -> Tuple[FakeKey, bool]:
             raise MissingKey(key=key_name)
         elif key is None:
             raise MissingVersion()
-        if key.storage_class in ARCHIVE_STORAGE_CLASSES:
+        if validate_storage_class and key.storage_class in ARCHIVE_STORAGE_CLASSES:
             if 'ongoing-request="false"' not in key.response_dict.get(
                 "x-amz-restore", ""
             ):


Test patch:
diff --git a/tests/test_s3/test_s3_storageclass.py b/tests/test_s3/test_s3_storageclass.py
index f40c97ee66ec..4ba0b321426a 100644
--- a/tests/test_s3/test_s3_storageclass.py
+++ b/tests/test_s3/test_s3_storageclass.py
@@ -269,3 +269,9 @@ def test_s3_get_object_from_glacier():
         "The operation is not valid for the object's storage class"
     )
     assert err["StorageClass"] == "GLACIER"
+
+    # Note that get_object_attributes should work
+    resp = s3_client.get_object_attributes(
+        Bucket=bucket_name, Key="test.txt", ObjectAttributes=["StorageClass"]
+    )
+    assert resp["StorageClass"] == "GLACIER"