feat: add IAM role support and remove django.contrib.sites dependency
- Add optional IAM role authentication for AWS SES backend - Remove AwsSesSettings model and sites framework dependency - Maintain backward compatibility with access key authentication - Update backend to conditionally use credentials when provided - Remove sites framework from installation requirements - Update documentation with IAM role configuration examples BREAKING CHANGE: Removes django.contrib.sites dependency and AwsSesSettings model
This commit is contained in:
14
README.md
14
README.md
@@ -70,14 +70,18 @@ SITE_ID = 1
|
||||
|
||||
Configure AWS SES credentials and the email backend:
|
||||
|
||||
### Option 1: IAM Role (Recommended for AWS environments)
|
||||
```python
|
||||
AWS_SES_ACCESS_KEY_ID = 'your-access-key-id' # Replace with your AWS IAM credentials
|
||||
AWS_SES_SECRET_ACCESS_KEY = 'your-secret-access-key'
|
||||
AWS_SES_REGION_NAME = 'us-east-1' # Adjust to your AWS SES region
|
||||
# No AWS credentials needed in settings
|
||||
AWS_SES_REGION_NAME = 'us-east-1'
|
||||
AWS_SES_REGION_ENDPOINT = 'https://email.us-east-1.amazonaws.com'
|
||||
|
||||
EMAIL_BACKEND = 'django_aws_ses.backends.SESBackend'
|
||||
DEFAULT_FROM_EMAIL = 'no-reply@yourdomain.com' # Verified in AWS SES
|
||||
DEFAULT_FROM_EMAIL = 'no-reply@yourdomain.com'
|
||||
```
|
||||
### Option 2: Access Keys
|
||||
```
|
||||
AWS_SES_ACCESS_KEY_ID = 'your-access-key-id'
|
||||
AWS_SES_SECRET_ACCESS_KEY = 'your-secret-access-key'
|
||||
```
|
||||
|
||||
Optional: Enable debugging logs for troubleshooting:
|
||||
|
||||
@@ -89,9 +89,6 @@ class SESBackend(BaseEmailBackend):
|
||||
self.dkim_selector = dkim_selector or settings.DKIM_SELECTOR
|
||||
self.dkim_headers = dkim_headers or settings.DKIM_HEADERS
|
||||
|
||||
if not (self._access_key_id and self._access_key):
|
||||
raise ImproperlyConfigured("AWS SES credentials are required.")
|
||||
|
||||
self.connection = None
|
||||
|
||||
def open(self):
|
||||
@@ -104,13 +101,22 @@ class SESBackend(BaseEmailBackend):
|
||||
return False
|
||||
|
||||
try:
|
||||
self.connection = boto3.client(
|
||||
'ses',
|
||||
aws_access_key_id=self._access_key_id,
|
||||
aws_secret_access_key=self._access_key,
|
||||
region_name=self._region_name,
|
||||
endpoint_url=self._endpoint_url,
|
||||
)
|
||||
|
||||
# Build client kwargs conditionally
|
||||
client_kwargs = {
|
||||
'service_name': 'ses',
|
||||
'region_name': self._region_name,
|
||||
'endpoint_url': self._endpoint_url,
|
||||
}
|
||||
|
||||
# Only add credentials if provided
|
||||
if self._access_key_id and self._access_key:
|
||||
client_kwargs.update({
|
||||
'aws_access_key_id': self._access_key_id,
|
||||
'aws_secret_access_key': self._access_key,
|
||||
})
|
||||
|
||||
self.connection = boto3.client(**client_kwargs)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to SES: {e}")
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
@@ -16,16 +16,6 @@ from django.core.signing import Signer, BadSignature
|
||||
User = get_user_model()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@receiver(post_save, sender=Site, dispatch_uid="update_awsses_settings")
|
||||
def update_awsses_settings(sender, instance, created, **kwargs):
|
||||
"""Create or update AwsSesSettings when a Site is saved."""
|
||||
try:
|
||||
if created:
|
||||
AwsSesSettings.objects.create(site=instance)
|
||||
instance.awssessettings.save()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save AwsSesSettings for site {instance.id}: {e}")
|
||||
|
||||
@receiver(post_save, sender=User, dispatch_uid="update_awsses_user")
|
||||
def update_awsses_user(sender, instance, created, **kwargs):
|
||||
"""Create or update AwsSesUserAddon when a User is saved."""
|
||||
@@ -38,7 +28,6 @@ def update_awsses_user(sender, instance, created, **kwargs):
|
||||
|
||||
class AwsSesSettings(models.Model):
|
||||
"""AWS SES configuration settings for a site."""
|
||||
site = models.OneToOneField(Site, on_delete=models.CASCADE, related_name='awssessettings')
|
||||
access_key = models.CharField(max_length=255, blank=True, null=True)
|
||||
secret_key = models.CharField(max_length=255, blank=True, null=True)
|
||||
region_name = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings as django_settings
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.utils import DatabaseError
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase, RequestFactory, override_settings
|
||||
from django.core.mail import EmailMessage
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
@@ -105,13 +105,19 @@ def dashboard(request):
|
||||
if cached_view:
|
||||
return cached_view
|
||||
|
||||
ses_conn = boto3.client(
|
||||
'ses',
|
||||
aws_access_key_id=settings.ACCESS_KEY,
|
||||
aws_secret_access_key=settings.SECRET_KEY,
|
||||
region_name=settings.AWS_SES_REGION_NAME,
|
||||
endpoint_url=settings.AWS_SES_REGION_ENDPOINT,
|
||||
)
|
||||
client_kwargs = {
|
||||
'service_name': 'ses',
|
||||
'region_name': settings.AWS_SES_REGION_NAME,
|
||||
'endpoint_url': settings.AWS_SES_REGION_ENDPOINT,
|
||||
}
|
||||
|
||||
if settings.ACCESS_KEY and settings.SECRET_KEY:
|
||||
client_kwargs.update({
|
||||
'aws_access_key_id': settings.ACCESS_KEY,
|
||||
'aws_secret_access_key': settings.SECRET_KEY,
|
||||
})
|
||||
|
||||
ses_conn = boto3.client(**client_kwargs)
|
||||
|
||||
try:
|
||||
quota_dict = ses_conn.get_send_quota()
|
||||
|
||||
Reference in New Issue
Block a user