Repository: getmoto/moto

Issue:
Getting objects with Storage Class = Glacier successfully
Hello,

I'm writing a test case to check if a functionality restoring an object from glacier works successfully or not.

However, getting an object with StorageClass='GLACIER' works successfully despite expectations of  returning an InvalidObjectStateError error as per documentation:
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.get_object

 
```
mock_s3_client.put_object(Bucket="bucket", Key="test.txt", Body="contents", StorageClass='GLACIER')
    response_get = mocked_s3_client.get_object(Bucket='bucket', Key='test.txt')
```



Discussion:
Hi @btruong66, welcome to Moto, and thanks for raising this!
I'll mark this as an enhancement to implement this validation.

Resolving patch:
diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -26,6 +26,10 @@
 <RangeRequested>{{ range_requested }}</RangeRequested>{% endblock %}
 """
 
+ERROR_WITH_STORAGE_CLASS = """{% extends 'single_error' %}
+{% block extra %}<StorageClass>{{ storage_class }}</StorageClass>{% endblock %}
+"""
+
 
 class S3ClientError(RESTError):
     # S3 API uses <RequestID> as the XML tag in response messages
@@ -501,6 +505,20 @@ def __init__(self, *args, **kwargs):
         )
 
 
+class InvalidObjectState(BucketError):
+    code = 400
+
+    def __init__(self, storage_class, **kwargs):
+        kwargs.setdefault("template", "storage_error")
+        self.templates["storage_error"] = ERROR_WITH_STORAGE_CLASS
+        super(BucketError, self).__init__(
+            error_type="InvalidObjectState",
+            message="The operation is not valid for the object's storage class",
+            storage_class=storage_class,
+            **kwargs,
+        )
+
+
 class LockNotEnabled(S3ClientError):
     code = 400
 
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -46,6 +46,7 @@
     IllegalLocationConstraintException,
     InvalidNotificationARN,
     InvalidNotificationEvent,
+    InvalidObjectState,
     ObjectNotInActiveTierError,
     NoSystemTags,
     PreconditionFailed,
@@ -1355,6 +1356,8 @@ def _key_response_get(self, bucket_name, query, key_name, headers):
         elif key is None:
             raise MissingVersion()
 
+        if key.storage_class == "GLACIER":
+            raise InvalidObjectState(storage_class="GLACIER")
         if if_unmodified_since:
             if_unmodified_since = str_to_rfc_1123_datetime(if_unmodified_since)
             if key.last_modified > if_unmodified_since:


Test patch:
diff --git a/tests/test_s3/test_s3_storageclass.py b/tests/test_s3/test_s3_storageclass.py
--- a/tests/test_s3/test_s3_storageclass.py
+++ b/tests/test_s3/test_s3_storageclass.py
@@ -247,3 +247,22 @@ def test_s3_copy_object_for_deep_archive_storage_class_restored():
     s3.head_object(Bucket="Bucket2", Key="Second_Object").should.not_have.property(
         "Restore"
     )
+
+
+@mock_s3
+def test_s3_get_object_from_glacier():
+    s3 = boto3.client("s3", region_name="us-east-1")
+    bucket_name = "tests3getobjectfromglacier"
+    s3.create_bucket(Bucket=bucket_name)
+
+    s3.put_object(
+        Bucket=bucket_name, Key="test.txt", Body="contents", StorageClass="GLACIER"
+    )
+    with pytest.raises(ClientError) as exc:
+        s3.get_object(Bucket=bucket_name, Key="test.txt")
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("InvalidObjectState")
+    err["Message"].should.equal(
+        "The operation is not valid for the object's storage class"
+    )
+    err["StorageClass"].should.equal("GLACIER")