Repository: getmoto/moto

Issue:
STS `get_federation_token` should raise on too long policy
AWS has a limit on the length of the policy that can be given to STS to get a temporary token (2048).

To reproduce:

```
import boto3
import json

policy = {'Version': '2012-10-17',
 'Statement': [
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137811/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137812/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137813/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137814/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137815/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137816/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137817/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137818/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137819/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137820/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137821/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137822/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137823/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137824/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137825/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137826/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137827/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137828/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137829/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137830/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137831/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137832/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137833/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137834/*'},
  {'Effect': 'Allow',
   'Action': ['s3:*'],
   'Resource': 'arn:aws:s3:::yyyy-xxxxx-cloud-default/user-83547/project-137835/*'}
]}

sts_cli = boto3.client('sts')
sts_cli.get_federation_token(Name='foo', DurationSeconds=3600, Policy=json.dumps(policy))
```

With STS, returns successfully with a set of temporary credentials

Should return:

```
ClientError: An error occurred (ValidationError) when calling the GetFederationToken operation: 1 validation error detected: Value '{"Version": "2012-10-17", "Statement": [...]}' at 'policy' failed to satisfy constraint: Member must have length less than or equal to 2048
```


Discussion:
Thanks; setting as a feature request.

Resolving patch:
diff --git a/moto/sts/exceptions.py b/moto/sts/exceptions.py
new file mode 100644
--- /dev/null
+++ b/moto/sts/exceptions.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+from moto.core.exceptions import RESTError
+
+
+class STSClientError(RESTError):
+    code = 400
+
+
+class STSValidationError(STSClientError):
+
+    def __init__(self, *args, **kwargs):
+        super(STSValidationError, self).__init__(
+            "ValidationError",
+            *args, **kwargs
+        )
diff --git a/moto/sts/responses.py b/moto/sts/responses.py
--- a/moto/sts/responses.py
+++ b/moto/sts/responses.py
@@ -1,8 +1,11 @@
 from __future__ import unicode_literals
 
 from moto.core.responses import BaseResponse
+from .exceptions import STSValidationError
 from .models import sts_backend
 
+MAX_FEDERATION_TOKEN_POLICY_LENGTH = 2048
+
 
 class TokenResponse(BaseResponse):
 
@@ -15,6 +18,15 @@ def get_session_token(self):
     def get_federation_token(self):
         duration = int(self.querystring.get('DurationSeconds', [43200])[0])
         policy = self.querystring.get('Policy', [None])[0]
+
+        if policy is not None and len(policy) > MAX_FEDERATION_TOKEN_POLICY_LENGTH:
+            raise STSValidationError(
+                "1 validation error detected: Value "
+                "'{\"Version\": \"2012-10-17\", \"Statement\": [...]}' "
+                "at 'policy' failed to satisfy constraint: Member must have length less than or "
+                " equal to %s" % MAX_FEDERATION_TOKEN_POLICY_LENGTH
+            )
+
         name = self.querystring.get('Name')[0]
         token = sts_backend.get_federation_token(
             duration=duration, name=name, policy=policy)


Test patch:
diff --git a/tests/test_sts/test_sts.py b/tests/test_sts/test_sts.py
--- a/tests/test_sts/test_sts.py
+++ b/tests/test_sts/test_sts.py
@@ -3,10 +3,13 @@
 
 import boto
 import boto3
+from botocore.client import ClientError
 from freezegun import freeze_time
+from nose.tools import assert_raises
 import sure  # noqa
 
 from moto import mock_sts, mock_sts_deprecated
+from moto.sts.responses import MAX_FEDERATION_TOKEN_POLICY_LENGTH
 
 
 @freeze_time("2012-01-01 12:00:00")
@@ -95,7 +98,7 @@ def test_assume_role_with_web_identity():
     })
     s3_role = "arn:aws:iam::123456789012:role/test-role"
     role = conn.assume_role_with_web_identity(
-            s3_role, "session-name", policy, duration_seconds=123)
+        s3_role, "session-name", policy, duration_seconds=123)
 
     credentials = role.credentials
     credentials.expiration.should.equal('2012-01-01T12:02:03.000Z')
@@ -117,3 +120,32 @@ def test_get_caller_identity():
     identity['Arn'].should.equal('arn:aws:sts::123456789012:user/moto')
     identity['UserId'].should.equal('AKIAIOSFODNN7EXAMPLE')
     identity['Account'].should.equal('123456789012')
+
+
+@mock_sts
+def test_federation_token_with_too_long_policy():
+    "Trying to get a federation token with a policy longer than 2048 character should fail"
+    cli = boto3.client("sts", region_name='us-east-1')
+    resource_tmpl = 'arn:aws:s3:::yyyy-xxxxx-cloud-default/my_default_folder/folder-name-%s/*'
+    statements = []
+    for num in range(30):
+        statements.append(
+            {
+                'Effect': 'Allow',
+                'Action': ['s3:*'],
+                'Resource': resource_tmpl % str(num)
+            }
+        )
+    policy = {
+        'Version': '2012-10-17',
+        'Statement': statements
+    }
+    json_policy = json.dumps(policy)
+    assert len(json_policy) > MAX_FEDERATION_TOKEN_POLICY_LENGTH
+
+    with assert_raises(ClientError) as exc:
+        cli.get_federation_token(Name='foo', DurationSeconds=3600, Policy=json_policy)
+    exc.exception.response['Error']['Code'].should.equal('ValidationError')
+    exc.exception.response['Error']['Message'].should.contain(
+        str(MAX_FEDERATION_TOKEN_POLICY_LENGTH)
+    )