-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #140 from stuartmaxwell:spf-records-mvp
Spf-records-mvp
- Loading branch information
Showing
22 changed files
with
1,138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,7 @@ | |
"timezone_converter", | ||
"markdown_editor", | ||
"shell", | ||
"spf_generator", | ||
] | ||
|
||
if DEBUG: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""SPF Generator App.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
"""Admin configuration for the SPF Generator app.""" | ||
|
||
from typing import ClassVar | ||
|
||
from django.contrib import admin | ||
from django.utils.html import format_html | ||
|
||
from .models import EmailProvider | ||
|
||
|
||
@admin.register(EmailProvider) | ||
class EmailProviderAdmin(admin.ModelAdmin): | ||
"""Admin interface configuration for EmailProvider model.""" | ||
|
||
list_display: ClassVar = [ | ||
"name", | ||
"category", | ||
"mechanism_display", | ||
"lookup_count", | ||
"priority", | ||
"active", | ||
] | ||
|
||
list_filter: ClassVar = [ | ||
"category", | ||
"active", | ||
"mechanism_type", | ||
] | ||
|
||
search_fields: ClassVar = [ | ||
"name", | ||
"description", | ||
"mechanism_value", | ||
"notes", | ||
] | ||
|
||
readonly_fields: ClassVar = [ | ||
"created_at", | ||
"updated_at", | ||
] | ||
|
||
fieldsets: ClassVar = [ | ||
( | ||
None, | ||
{ | ||
"fields": [ | ||
"name", | ||
"category", | ||
"description", | ||
], | ||
}, | ||
), | ||
( | ||
"SPF Configuration", | ||
{ | ||
"fields": [ | ||
"mechanism_type", | ||
"mechanism_value", | ||
"lookup_count", | ||
"priority", | ||
], | ||
}, | ||
), | ||
( | ||
"Status", | ||
{ | ||
"fields": [ | ||
"active", | ||
"notes", | ||
], | ||
}, | ||
), | ||
( | ||
"Metadata", | ||
{ | ||
"classes": ["collapse"], | ||
"fields": [ | ||
"created_at", | ||
"updated_at", | ||
], | ||
}, | ||
), | ||
] | ||
|
||
@admin.display(description="Mechanism") | ||
def mechanism_display(self, obj: EmailProvider) -> str: | ||
"""Displays the complete SPF mechanism in the list view. | ||
Args: | ||
obj: The EmailProvider instance | ||
Returns: | ||
str: HTML-formatted SPF mechanism | ||
""" | ||
return format_html( | ||
"<code>{}</code>", | ||
obj.get_mechanism(), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""App configuration for the SPF Generator app.""" | ||
|
||
from django.apps import AppConfig | ||
|
||
|
||
class SpfGeneratorConfig(AppConfig): | ||
"""App configuration for the SPF Generator app.""" | ||
|
||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "spf_generator" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
"""Forms for the spf_generator app.""" | ||
|
||
import ipaddress | ||
from typing import Any | ||
|
||
from django import forms | ||
from django.core.exceptions import ValidationError | ||
|
||
from spf_generator.models import EmailProvider, ProviderCategory, SpfAllMechanism | ||
|
||
|
||
def validate_ip_address(value: str) -> None: | ||
"""Validates if the string is a valid IPv4 or IPv6 address. | ||
Args: | ||
value: String to validate | ||
Raises: | ||
ValidationError: If the string is not a valid IP address | ||
""" | ||
if not value: | ||
return | ||
|
||
try: | ||
ipaddress.ip_address(value) | ||
except ValueError as exc: | ||
msg = "Please enter a valid IP address" | ||
raise ValidationError(msg) from exc | ||
|
||
|
||
class ProviderSelectForm(forms.Form): | ||
"""Form for selecting email providers. | ||
The form dynamically generates checkboxes for each active provider, | ||
grouped by category. | ||
""" | ||
|
||
all_mechanism = forms.ChoiceField( | ||
choices=SpfAllMechanism.choices, | ||
initial=SpfAllMechanism.FAIL, | ||
required=True, | ||
help_text=( | ||
"<p>Choose how to handle mail from unlisted servers:</p>" | ||
"<ul>" | ||
"<li><strong>Fail (-all)</strong>: Recommended. Explicitly reject mail from unlisted servers. " | ||
"Use this if you're sure you've listed all legitimate sending servers.</li>" | ||
"<li><strong>Softfail (~all)</strong>: Suggest rejection but don't enforce it. " | ||
"Useful during SPF testing or if you're unsure about all legitimate senders.</li>" | ||
"<li><strong>Neutral (?all)</strong>: Take no position on unlisted servers. " | ||
"Not recommended as it doesn't help prevent email spoofing.</li>" | ||
"</ul>" | ||
), | ||
) | ||
|
||
custom_ip = forms.CharField( | ||
required=False, | ||
validators=[validate_ip_address], | ||
help_text="If you have a server that sends email, enter its IP address here", | ||
widget=forms.TextInput( | ||
attrs={ | ||
"placeholder": "e.g., 192.168.1.1 or 2001:db8::1", | ||
"aria-label": "Custom IP Address", | ||
}, | ||
), | ||
) | ||
|
||
def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401 | ||
"""Initializes the form with provider fields.""" | ||
super().__init__(*args, **kwargs) | ||
|
||
# Group providers by category | ||
for category in ProviderCategory.choices: | ||
providers = EmailProvider.objects.filter( | ||
category=category[0], | ||
active=True, | ||
) | ||
|
||
for provider in providers: | ||
field_name = f"provider_{provider.id}" | ||
self.fields[field_name] = forms.BooleanField( | ||
required=False, | ||
label=provider.name, | ||
help_text=provider.mechanism_value, | ||
) | ||
|
||
def clean_custom_ip(self) -> str: | ||
"""Clean and validate the custom IP address. | ||
Returns: | ||
str: The cleaned IP address or empty string | ||
""" | ||
ip = self.cleaned_data.get("custom_ip", "").strip() | ||
if ip: | ||
try: | ||
# Determine if IPv4 or IPv6 and format accordingly | ||
ip_obj = ipaddress.ip_address(ip) | ||
except ValueError as exc: | ||
msg = "Please enter a valid IP address" | ||
raise ValidationError(msg) from exc | ||
else: | ||
if isinstance(ip_obj, ipaddress.IPv6Address): | ||
return f"ip6:{ip}" | ||
return f"ip4:{ip}" | ||
return "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Management commands for the SPF generator.""" |
Oops, something went wrong.