Repository: getmoto/moto

Issue:
SES get_dkim_tokens creates duplicate domain
# Steps to reproduce:

```
from moto import mock_ses
import boto3
import unittest

class TestMoto(unittest.TestCase):
    def test_moto(self):
        domain = 'example.com'
        with mock_ses():
            client = boto3.client('ses')
        
            response = client.list_identities(IdentityType='Domain')
            self.assertEqual(sum([(domain == i) for i in response.get('Identities', [])]), 0)
        
            ret = client.verify_domain_identity(Domain=domain)
            response = client.list_identities(IdentityType='Domain')
            self.assertEqual(sum([(domain == i) for i in response.get('Identities', [])]), 1)
        
            response = client.verify_domain_dkim(Domain=domain)
            response = client.list_identities(IdentityType='Domain')
            self.assertEqual(sum([(domain == i) for i in response.get('Identities', [])]), 1)
                
```

Save as `test.py` and run with `python3 -m unittest test.py`

# Expected behavior

The test passes.

i.e. when `verify_domain_dkim` is called, it checks whether the domain already exists in the list,
and doesn't re-add it as a duplicate.

To confirm the expected behavior, comment out `with mock_ses()` and run against real AWS.

# Actual behavior

The script fails. The final assertion fails.

`verify_domain_dkim` seems to have created a second domain, without checking whether it's already added.


Discussion:
Thanks for raising this and for providing the test case @mdavis-xyz - that's very helpful. 
Looks like we're storing domains as a simple list, instead of as a set. Marking it as a bug.

Resolving patch:
diff --git a/moto/ses/models.py b/moto/ses/models.py
--- a/moto/ses/models.py
+++ b/moto/ses/models.py
@@ -137,7 +137,8 @@ def verify_email_address(self, address):
         self.email_addresses.append(address)
 
     def verify_domain(self, domain):
-        self.domains.append(domain)
+        if domain.lower() not in self.domains:
+            self.domains.append(domain.lower())
 
     def list_identities(self):
         return self.domains + self.addresses


Test patch:
diff --git a/tests/test_ses/test_ses_boto3.py b/tests/test_ses/test_ses_boto3.py
--- a/tests/test_ses/test_ses_boto3.py
+++ b/tests/test_ses/test_ses_boto3.py
@@ -8,7 +8,7 @@
 import pytest
 
 
-# import sure  # noqa
+import sure  # noqa
 
 from moto import mock_ses
 
@@ -598,3 +598,19 @@ def test_update_ses_template():
     result["Template"]["HtmlPart"].should.equal(
         "<h1>Hello {{name}},</h1><p>Your favorite color is {{color}}</p>"
     )
+
+
+@mock_ses
+def test_domains_are_case_insensitive():
+    client = boto3.client("ses", region_name="us-east-1")
+    duplicate_domains = [
+        "EXAMPLE.COM",
+        "EXAMple.Com",
+        "example.com",
+    ]
+    for domain in duplicate_domains:
+        client.verify_domain_identity(Domain=domain)
+        client.verify_domain_dkim(Domain=domain)
+        identities = client.list_identities(IdentityType="Domain")["Identities"]
+        identities.should.have.length_of(1)
+        identities[0].should.equal("example.com")