django-solomon 0.4.2__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {django_solomon-0.4.2 → django_solomon-0.5.0}/CHANGELOG.md +11 -15
  2. {django_solomon-0.4.2 → django_solomon-0.5.0}/PKG-INFO +16 -7
  3. {django_solomon-0.4.2 → django_solomon-0.5.0}/README.md +14 -5
  4. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/installation.md +16 -4
  5. django_solomon-0.5.0/docs/models.md +145 -0
  6. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/settings.md +34 -23
  7. {django_solomon-0.4.2 → django_solomon-0.5.0}/justfile +2 -1
  8. {django_solomon-0.4.2 → django_solomon-0.5.0}/mkdocs.yml +1 -0
  9. {django_solomon-0.4.2 → django_solomon-0.5.0}/pyproject.toml +3 -7
  10. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/admin.py +36 -1
  11. django_solomon-0.5.0/src/django_solomon/apps.py +10 -0
  12. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/backends.py +3 -2
  13. django_solomon-0.5.0/src/django_solomon/checks.py +42 -0
  14. django_solomon-0.5.0/src/django_solomon/forms.py +27 -0
  15. django_solomon-0.5.0/src/django_solomon/migrations/0001_initial.py +73 -0
  16. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/models.py +56 -7
  17. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/utilities.py +3 -4
  18. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/settings.py +21 -0
  19. django_solomon-0.5.0/tests/test_admin.py +313 -0
  20. django_solomon-0.5.0/tests/test_backends.py +212 -0
  21. django_solomon-0.5.0/tests/test_checks.py +48 -0
  22. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/test_config.py +23 -21
  23. django_solomon-0.5.0/tests/test_forms.py +226 -0
  24. django_solomon-0.5.0/tests/test_models.py +271 -0
  25. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/test_utilities.py +72 -70
  26. django_solomon-0.5.0/tests/test_views.py +340 -0
  27. {django_solomon-0.4.2 → django_solomon-0.5.0}/tox.ini +2 -1
  28. {django_solomon-0.4.2 → django_solomon-0.5.0}/uv.lock +38 -122
  29. django_solomon-0.4.2/src/django_solomon/apps.py +0 -31
  30. django_solomon-0.4.2/src/django_solomon/forms.py +0 -12
  31. django_solomon-0.4.2/src/django_solomon/migrations/0001_initial.py +0 -47
  32. django_solomon-0.4.2/src/django_solomon/migrations/0002_magiclink_ip_address.py +0 -18
  33. django_solomon-0.4.2/src/django_solomon/migrations/0003_add_unique_constraint_auth_user_email.py +0 -43
  34. django_solomon-0.4.2/tests/conftest.py +0 -55
  35. django_solomon-0.4.2/tests/test_admin.py +0 -228
  36. django_solomon-0.4.2/tests/test_backends.py +0 -224
  37. django_solomon-0.4.2/tests/test_forms.py +0 -127
  38. django_solomon-0.4.2/tests/test_migrations.py +0 -77
  39. django_solomon-0.4.2/tests/test_models.py +0 -265
  40. django_solomon-0.4.2/tests/test_views.py +0 -305
  41. {django_solomon-0.4.2 → django_solomon-0.5.0}/.forgejo/workflows/release.yml +0 -0
  42. {django_solomon-0.4.2 → django_solomon-0.5.0}/.forgejo/workflows/tests.yml +0 -0
  43. {django_solomon-0.4.2 → django_solomon-0.5.0}/.gitignore +0 -0
  44. {django_solomon-0.4.2 → django_solomon-0.5.0}/.pre-commit-config.yaml +0 -0
  45. {django_solomon-0.4.2 → django_solomon-0.5.0}/.readthedocs.yml +0 -0
  46. {django_solomon-0.4.2 → django_solomon-0.5.0}/LICENSE +0 -0
  47. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/changelog.md +0 -0
  48. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/contributing.md +0 -0
  49. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/index.md +0 -0
  50. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/requirements.txt +0 -0
  51. {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/templates.md +0 -0
  52. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/__init__.py +0 -0
  53. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/config.py +0 -0
  54. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/locale/de/LC_MESSAGES/django.po +0 -0
  55. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/locale/en/LC_MESSAGES/django.po +0 -0
  56. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/management/__init__.py +0 -0
  57. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/management/commands/__init__.py +0 -0
  58. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/migrations/__init__.py +0 -0
  59. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/py.typed +0 -0
  60. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/invalid_magic_link.html +0 -0
  61. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/login_form.html +0 -0
  62. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/magic_link_sent.html +0 -0
  63. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/email/magic_link.mjml +0 -0
  64. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/email/magic_link.txt +0 -0
  65. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/urls.py +0 -0
  66. {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/views.py +0 -0
  67. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/.gitignore +0 -0
  68. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/__init__.py +0 -0
  69. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/templates/base.html +0 -0
  70. {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/test_urls.py +0 -0
@@ -5,25 +5,21 @@ All notable changes to django-solomon will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [0.4.2] - 2025-04-21
8
+ ## [0.5.0] - 2025-04-21
9
9
 
10
- ### Improved
11
-
12
- - Enhanced case-insensitive email uniqueness constraint implementation
13
- - Added regression test to verify constraint behavior
14
-
15
- ## [0.4.1] - 2025-04-20
16
-
17
- ### Fixed
18
-
19
- - Resolved issue with Django migration system detecting duplicate constraints
20
- - Prevented unnecessary migration files from being created
10
+ ### Added
21
11
 
22
- ## [0.4.0] - 2025-04-20
12
+ - Added a custom user model that implements the `AbstractUser` model
13
+ - System check to validate that the user model has a unique email field
14
+ - Comprehensive test suite for system checks
15
+ - Improved documentation for CustomUser model
16
+ - Added documentation for system checks
17
+ - New models.md documentation file
18
+ - Updated README to mention CustomUser model and system checks
23
19
 
24
- ### Added
20
+ ## [0.4.x] - 2025-04-20
25
21
 
26
- - Unique constraint on auth.User email field (case-insensitive)
22
+ - All versions of the 0.4.x series were yanked due to a implemention error.
27
23
 
28
24
  ## [0.3.0] - 2025-04-20
29
25
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-solomon
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: A Django app for passwordless authentication using magic links.
5
5
  Project-URL: Home, https://django-solomon.rtfd.io/
6
6
  Project-URL: Documentation, https://django-solomon.rtfd.io/
@@ -30,7 +30,7 @@ License: MIT License
30
30
  SOFTWARE.
31
31
  License-File: LICENSE
32
32
  Keywords: django
33
- Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Development Status :: 4 - Beta
34
34
  Classifier: Environment :: Web Environment
35
35
  Classifier: Framework :: Django :: 4.2
36
36
  Classifier: Framework :: Django :: 5.0
@@ -65,6 +65,7 @@ A Django app for passwordless authentication using magic links.
65
65
  ## Features
66
66
 
67
67
  - Passwordless authentication using magic links sent via email
68
+ - Email-based authentication with a built-in CustomUser model
68
69
  - Configurable link expiration time
69
70
  - Blacklist functionality to block specific email addresses
70
71
  - Support for auto-creating users when they request a magic link
@@ -72,7 +73,7 @@ A Django app for passwordless authentication using magic links.
72
73
  - Privacy-focused IP anonymization
73
74
  - Customizable templates for emails and pages
74
75
  - Compatible with Django's authentication system
75
- - Enforces unique email addresses for users (case-insensitive)
76
+ - System checks to ensure proper configuration
76
77
 
77
78
  ## Installation
78
79
 
@@ -92,7 +93,15 @@ INSTALLED_APPS = [
92
93
  ]
93
94
  ```
94
95
 
95
- 2. Add the authentication backend to your settings:
96
+ 2. Configure the user model in your settings:
97
+
98
+ ```python
99
+ AUTH_USER_MODEL = 'django_solomon.CustomUser'
100
+ ```
101
+
102
+ This setting is required for django-solomon to work properly, as it needs a user model with a unique email field for email-based authentication. You can also use your own custom user model as long as it has a unique email field.
103
+
104
+ 3. Add the authentication backend to your settings:
96
105
 
97
106
  ```python
98
107
  AUTHENTICATION_BACKENDS = [
@@ -101,7 +110,7 @@ AUTHENTICATION_BACKENDS = [
101
110
  ]
102
111
  ```
103
112
 
104
- 3. Include the django-solomon URLs in your project's `urls.py`:
113
+ 4. Include the django-solomon URLs in your project's `urls.py`:
105
114
 
106
115
  ```python
107
116
  from django.urls import include, path
@@ -113,7 +122,7 @@ urlpatterns = [
113
122
  ]
114
123
  ```
115
124
 
116
- 4. Set the login URL in your settings to use django-solomon's login view:
125
+ 5. Set the login URL in your settings to use django-solomon's login view:
117
126
 
118
127
  ```python
119
128
  LOGIN_URL = 'django_solomon:login'
@@ -121,7 +130,7 @@ LOGIN_URL = 'django_solomon:login'
121
130
 
122
131
  This ensures that when users need to authenticate, they'll be redirected to the magic link login page.
123
132
 
124
- 5. Configure your email settings to ensure emails can be sent:
133
+ 6. Configure your email settings to ensure emails can be sent:
125
134
 
126
135
  ```python
127
136
  EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
@@ -13,6 +13,7 @@ A Django app for passwordless authentication using magic links.
13
13
  ## Features
14
14
 
15
15
  - Passwordless authentication using magic links sent via email
16
+ - Email-based authentication with a built-in CustomUser model
16
17
  - Configurable link expiration time
17
18
  - Blacklist functionality to block specific email addresses
18
19
  - Support for auto-creating users when they request a magic link
@@ -20,7 +21,7 @@ A Django app for passwordless authentication using magic links.
20
21
  - Privacy-focused IP anonymization
21
22
  - Customizable templates for emails and pages
22
23
  - Compatible with Django's authentication system
23
- - Enforces unique email addresses for users (case-insensitive)
24
+ - System checks to ensure proper configuration
24
25
 
25
26
  ## Installation
26
27
 
@@ -40,7 +41,15 @@ INSTALLED_APPS = [
40
41
  ]
41
42
  ```
42
43
 
43
- 2. Add the authentication backend to your settings:
44
+ 2. Configure the user model in your settings:
45
+
46
+ ```python
47
+ AUTH_USER_MODEL = 'django_solomon.CustomUser'
48
+ ```
49
+
50
+ This setting is required for django-solomon to work properly, as it needs a user model with a unique email field for email-based authentication. You can also use your own custom user model as long as it has a unique email field.
51
+
52
+ 3. Add the authentication backend to your settings:
44
53
 
45
54
  ```python
46
55
  AUTHENTICATION_BACKENDS = [
@@ -49,7 +58,7 @@ AUTHENTICATION_BACKENDS = [
49
58
  ]
50
59
  ```
51
60
 
52
- 3. Include the django-solomon URLs in your project's `urls.py`:
61
+ 4. Include the django-solomon URLs in your project's `urls.py`:
53
62
 
54
63
  ```python
55
64
  from django.urls import include, path
@@ -61,7 +70,7 @@ urlpatterns = [
61
70
  ]
62
71
  ```
63
72
 
64
- 4. Set the login URL in your settings to use django-solomon's login view:
73
+ 5. Set the login URL in your settings to use django-solomon's login view:
65
74
 
66
75
  ```python
67
76
  LOGIN_URL = 'django_solomon:login'
@@ -69,7 +78,7 @@ LOGIN_URL = 'django_solomon:login'
69
78
 
70
79
  This ensures that when users need to authenticate, they'll be redirected to the magic link login page.
71
80
 
72
- 5. Configure your email settings to ensure emails can be sent:
81
+ 6. Configure your email settings to ensure emails can be sent:
73
82
 
74
83
  ```python
75
84
  EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
@@ -69,7 +69,19 @@ INSTALLED_APPS = [
69
69
  ]
70
70
  ```
71
71
 
72
- ### 2. Configure Authentication Backend
72
+ ### 2. Configure User Model
73
+
74
+ django-solomon requires a user model with a unique email field for email-based authentication. You can either use the provided CustomUser model or your own custom model that meets this requirement.
75
+
76
+ To use the CustomUser model from django-solomon, add the following to your settings:
77
+
78
+ ```python
79
+ AUTH_USER_MODEL = 'django_solomon.CustomUser'
80
+ ```
81
+
82
+ If you're using your own custom user model, ensure it has a unique email field.
83
+
84
+ ### 3. Configure Authentication Backend
73
85
 
74
86
  Add the authentication backend to your settings:
75
87
 
@@ -80,7 +92,7 @@ AUTHENTICATION_BACKENDS = [
80
92
  ]
81
93
  ```
82
94
 
83
- ### 3. Include URLs
95
+ ### 4. Include URLs
84
96
 
85
97
  Include the django-solomon URLs in your project's `urls.py`:
86
98
 
@@ -94,7 +106,7 @@ urlpatterns = [
94
106
  ]
95
107
  ```
96
108
 
97
- ### 4. Set Login URL
109
+ ### 5. Set Login URL
98
110
 
99
111
  Set the login URL in your settings to use django-solomon's login view:
100
112
 
@@ -104,7 +116,7 @@ LOGIN_URL = 'django_solomon:login'
104
116
 
105
117
  This ensures that when users need to authenticate, they'll be redirected to the magic link login page.
106
118
 
107
- ### 5. Configure Email Settings
119
+ ### 6. Configure Email Settings
108
120
 
109
121
  Configure your email settings to ensure magic link emails can be sent:
110
122
 
@@ -0,0 +1,145 @@
1
+ ---
2
+ hide:
3
+ - navigation
4
+ ---
5
+
6
+ # Models and System Checks
7
+
8
+ This guide provides detailed information about the models and system checks in django-solomon.
9
+
10
+ ## Models
11
+
12
+ ### CustomUser
13
+
14
+ django-solomon includes a `CustomUser` model that extends Django's `AbstractUser` model to provide email-based authentication.
15
+
16
+ #### Features
17
+
18
+ - Uses email as the unique identifier for authentication instead of username
19
+ - Automatically sets the username to the email address
20
+ - Includes a custom manager (`CustomUserManager`) for creating users and superusers
21
+
22
+ #### Implementation
23
+
24
+ ```python
25
+ class CustomUser(AbstractUser):
26
+ email = models.EmailField(_("email address"), unique=True)
27
+
28
+ USERNAME_FIELD = "email"
29
+ REQUIRED_FIELDS = ["username"]
30
+
31
+ objects = CustomUserManager()
32
+
33
+ def __str__(self):
34
+ return self.email
35
+
36
+ def save(self, *args, **kwargs):
37
+ self.username = self.email
38
+ super().save(*args, **kwargs)
39
+ ```
40
+
41
+ #### CustomUserManager
42
+
43
+ The `CustomUserManager` provides methods for creating users and superusers with email-based authentication:
44
+
45
+ ```python
46
+ class CustomUserManager(BaseUserManager):
47
+ """
48
+ Custom user model manager where email is the unique identifiers
49
+ for authentication instead of usernames.
50
+ """
51
+
52
+ def create_user(self, email, password, **extra_fields):
53
+ """
54
+ Create and save a user with the given email and password.
55
+ """
56
+ if not email:
57
+ raise ValueError(_("The Email must be set"))
58
+ email = self.normalize_email(email)
59
+ user = self.model(email=email, **extra_fields)
60
+ user.set_password(password)
61
+ user.save()
62
+ return user
63
+
64
+ def create_superuser(self, email, password, **extra_fields):
65
+ """
66
+ Create and save a SuperUser with the given email and password.
67
+ """
68
+ extra_fields.setdefault("is_staff", True)
69
+ extra_fields.setdefault("is_superuser", True)
70
+ extra_fields.setdefault("is_active", True)
71
+
72
+ if extra_fields.get("is_staff") is not True:
73
+ raise ValueError(_("Superuser must have is_staff=True."))
74
+ if extra_fields.get("is_superuser") is not True:
75
+ raise ValueError(_("Superuser must have is_superuser=True."))
76
+ return self.create_user(email, password, **extra_fields)
77
+ ```
78
+
79
+ ### MagicLink
80
+
81
+ The `MagicLink` model is used to store magic links for authentication. It includes:
82
+
83
+ - A reference to the user
84
+ - A unique token
85
+ - Creation and expiration timestamps
86
+ - A flag to track if the link has been used
87
+ - Optional IP address tracking
88
+
89
+ ### BlacklistedEmail
90
+
91
+ The `BlacklistedEmail` model is used to store email addresses that are blocked from using the magic login feature.
92
+
93
+ ## System Checks
94
+
95
+ django-solomon includes system checks to ensure that your Django project is properly configured to work with the package.
96
+
97
+ ### User Model Email Check
98
+
99
+ The `check_user_model_email` function checks that the user model has a unique email field, which is required for email-based authentication.
100
+
101
+ ```python
102
+ @register()
103
+ def check_user_model_email(app_configs, **kwargs):
104
+ """
105
+ Check that the user model has a unique email field.
106
+ """
107
+ errors = []
108
+
109
+ # Get the user model
110
+ user_model = apps.get_model(settings.AUTH_USER_MODEL)
111
+
112
+ # Check if email field exists
113
+ try:
114
+ email_field = user_model._meta.get_field("email")
115
+ except Exception:
116
+ errors.append(
117
+ Error(
118
+ "User model does not have an email field.",
119
+ hint="Make sure your user model has an email field.",
120
+ obj=settings.AUTH_USER_MODEL,
121
+ id="django_solomon.E001",
122
+ )
123
+ )
124
+ return errors
125
+
126
+ # Check if email field is unique
127
+ if not email_field.unique:
128
+ errors.append(
129
+ Error(
130
+ "User model email field must be unique.",
131
+ hint="Set unique=True on the email field of your user model.",
132
+ obj=settings.AUTH_USER_MODEL,
133
+ id="django_solomon.E002",
134
+ )
135
+ )
136
+
137
+ return errors
138
+ ```
139
+
140
+ This check ensures that:
141
+
142
+ 1. The user model has an email field
143
+ 2. The email field is set as unique
144
+
145
+ These requirements are essential for the email-based authentication that django-solomon provides.
@@ -9,7 +9,8 @@ This guide provides detailed information about all the configurable settings in
9
9
 
10
10
  ## Overview
11
11
 
12
- django-solomon provides several settings that you can customize in your Django settings file. These settings control various aspects of the magic link authentication system, including link expiration, user creation, templates, and more.
12
+ django-solomon provides several settings that you can customize in your Django settings file. These settings control
13
+ various aspects of the magic link authentication system, including link expiration, user creation, templates, and more.
13
14
 
14
15
  ## Core Settings
15
16
 
@@ -28,7 +29,8 @@ SOLOMON_LINK_EXPIRATION = 600
28
29
 
29
30
  **Default:** `True`
30
31
 
31
- If enabled, only one active magic link is allowed per user. When a new magic link is requested, any existing active links for the same user will be marked as used.
32
+ If enabled, only one active magic link is allowed per user. When a new magic link is requested, any existing active
33
+ links for the same user will be marked as used.
32
34
 
33
35
  ```python
34
36
  # Allow multiple active magic links per user
@@ -63,7 +65,8 @@ SOLOMON_LOGIN_REDIRECT_URL = '/dashboard/'
63
65
 
64
66
  **Default:** `True`
65
67
 
66
- If enabled, allows superusers (admin users) to log in using magic links. If disabled, superusers will need to use the standard Django admin login.
68
+ If enabled, allows superusers (admin users) to log in using magic links. If disabled, superusers will need to use the
69
+ standard Django admin login.
67
70
 
68
71
  ```python
69
72
  # Disable magic link authentication for superusers
@@ -74,7 +77,8 @@ SOLOMON_ALLOW_ADMIN_LOGIN = False
74
77
 
75
78
  **Default:** `True`
76
79
 
77
- If enabled, allows staff users to log in using magic links. If disabled, staff users will need to use the standard Django admin login.
80
+ If enabled, allows staff users to log in using magic links. If disabled, staff users will need to use the standard
81
+ Django admin login.
78
82
 
79
83
  ```python
80
84
  # Disable magic link authentication for staff users
@@ -89,7 +93,8 @@ django-solomon can track and validate IP addresses for enhanced security while r
89
93
 
90
94
  **Default:** `False`
91
95
 
92
- If enabled, validates that magic links are used from the same IP address they were created from. This adds an extra layer of security by preventing magic links from being used if intercepted by an attacker on a different network.
96
+ If enabled, validates that magic links are used from the same IP address they were created from. This adds an extra
97
+ layer of security by preventing magic links from being used if intercepted by an attacker on a different network.
93
98
 
94
99
  ```python
95
100
  # Enable IP validation for magic links
@@ -100,7 +105,9 @@ SOLOMON_ENFORCE_SAME_IP = True
100
105
 
101
106
  **Default:** `True`
102
107
 
103
- If enabled, anonymizes IP addresses before storing them. For IPv4 addresses, the last octet is removed (e.g., 192.168.1.1 becomes 192.168.1.0). For IPv6 addresses, the last 80 bits (last 5 segments) are removed. This enhances user privacy while still allowing for IP validation.
108
+ If enabled, anonymizes IP addresses before storing them. For IPv4 addresses, the last octet is removed (e.g.,
109
+ 192.168.1.1 becomes 192.168.1.0). For IPv6 addresses, the last 80 bits (last 5 segments) are removed. This enhances user
110
+ privacy while still allowing for IP validation.
104
111
 
105
112
  ```python
106
113
  # Disable IP anonymization (store full IP addresses)
@@ -109,7 +116,8 @@ SOLOMON_ANONYMIZE_IP = False
109
116
 
110
117
  ## Template Settings
111
118
 
112
- django-solomon uses several templates for rendering emails and pages. You can customize these templates by providing your own versions.
119
+ django-solomon uses several templates for rendering emails and pages. You can customize these templates by providing
120
+ your own versions.
113
121
 
114
122
  ### SOLOMON_MAIL_TEXT_TEMPLATE
115
123
 
@@ -126,7 +134,8 @@ SOLOMON_MAIL_TEXT_TEMPLATE = "myapp/emails/magic_link.txt"
126
134
 
127
135
  **Default:** `"django_solomon/email/magic_link.mjml"`
128
136
 
129
- The template to use for HTML magic link emails (MJML format). django-solomon uses MJML for creating responsive HTML emails.
137
+ The template to use for HTML magic link emails (MJML format). django-solomon uses MJML for creating responsive HTML
138
+ emails.
130
139
 
131
140
  ```python
132
141
  # Use a custom MJML email template
@@ -148,7 +157,8 @@ SOLOMON_LOGIN_FORM_TEMPLATE = "myapp/auth/login_form.html"
148
157
 
149
158
  **Default:** `"django_solomon/base/invalid_magic_link.html"`
150
159
 
151
- The template to use for the invalid magic link page, which is shown when a user tries to use an expired or already used magic link.
160
+ The template to use for the invalid magic link page, which is shown when a user tries to use an expired or already used
161
+ magic link.
152
162
 
153
163
  ```python
154
164
  # Use a custom invalid magic link template
@@ -168,7 +178,8 @@ SOLOMON_MAGIC_LINK_SENT_TEMPLATE = "myapp/auth/link_sent.html"
168
178
 
169
179
  ## Email Settings
170
180
 
171
- django-solomon uses Django's email system to send magic links. You should configure Django's email settings in your settings file:
181
+ django-solomon uses Django's email system to send magic links. You should configure Django's email settings in your
182
+ settings file:
172
183
 
173
184
  ```python
174
185
  # Example email configuration for Gmail
@@ -191,18 +202,18 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
191
202
 
192
203
  Here's a summary of all available settings:
193
204
 
194
- | Setting | Default | Description |
195
- |---------------------------------------|-------------------------------------------------|----------------------------------------------------------------------------------------|
196
- | `SOLOMON_LINK_EXPIRATION` | `300` | The expiration time for magic links in seconds |
197
- | `SOLOMON_ONLY_ONE_LINK_ALLOWED` | `True` | If enabled, only one active magic link is allowed per user |
198
- | `SOLOMON_CREATE_USER_IF_NOT_FOUND` | `False` | If enabled, creates a new user when a magic link is requested for a non-existent email |
199
- | `SOLOMON_LOGIN_REDIRECT_URL` | `settings.LOGIN_REDIRECT_URL` | The URL to redirect to after successful authentication |
200
- | `SOLOMON_ALLOW_ADMIN_LOGIN` | `True` | If enabled, allows superusers to log in using magic links |
201
- | `SOLOMON_ALLOW_STAFF_LOGIN` | `True` | If enabled, allows staff users to log in using magic links |
202
- | `SOLOMON_MAIL_TEXT_TEMPLATE` | `"django_solomon/email/magic_link.txt"` | The template to use for plain text magic link emails |
203
- | `SOLOMON_MAIL_MJML_TEMPLATE` | `"django_solomon/email/magic_link.mjml"` | The template to use for HTML magic link emails (MJML format) |
204
- | `SOLOMON_LOGIN_FORM_TEMPLATE` | `"django_solomon/base/login_form.html"` | The template to use for the login form page |
205
- | `SOLOMON_INVALID_MAGIC_LINK_TEMPLATE` | `"django_solomon/base/invalid_magic_link.html"` | The template to use for the invalid magic link page |
206
- | `SOLOMON_MAGIC_LINK_SENT_TEMPLATE` | `"django_solomon/base/magic_link_sent.html"` | The template to use for the magic link sent confirmation page |
205
+ | Setting | Default | Description |
206
+ |---------------------------------------|-------------------------------------------------|-----------------------------------------------------------------------------------------|
207
+ | `SOLOMON_LINK_EXPIRATION` | `300` | The expiration time for magic links in seconds |
208
+ | `SOLOMON_ONLY_ONE_LINK_ALLOWED` | `True` | If enabled, only one active magic link is allowed per user |
209
+ | `SOLOMON_CREATE_USER_IF_NOT_FOUND` | `False` | If enabled, creates a new user when a magic link is requested for a non-existent email |
210
+ | `SOLOMON_LOGIN_REDIRECT_URL` | `settings.LOGIN_REDIRECT_URL` | The URL to redirect to after successful authentication |
211
+ | `SOLOMON_ALLOW_ADMIN_LOGIN` | `True` | If enabled, allows superusers to log in using magic links |
212
+ | `SOLOMON_ALLOW_STAFF_LOGIN` | `True` | If enabled, allows staff users to log in using magic links |
213
+ | `SOLOMON_MAIL_TEXT_TEMPLATE` | `"django_solomon/email/magic_link.txt"` | The template to use for plain text magic link emails |
214
+ | `SOLOMON_MAIL_MJML_TEMPLATE` | `"django_solomon/email/magic_link.mjml"` | The template to use for HTML magic link emails (MJML format) |
215
+ | `SOLOMON_LOGIN_FORM_TEMPLATE` | `"django_solomon/base/login_form.html"` | The template to use for the login form page |
216
+ | `SOLOMON_INVALID_MAGIC_LINK_TEMPLATE` | `"django_solomon/base/invalid_magic_link.html"` | The template to use for the invalid magic link page |
217
+ | `SOLOMON_MAGIC_LINK_SENT_TEMPLATE` | `"django_solomon/base/magic_link_sent.html"` | The template to use for the magic link sent confirmation page |
207
218
  | `SOLOMON_ENFORCE_SAME_IP` | `False` | If enabled, validates that magic links are used from the same IP they were created from |
208
219
  | `SOLOMON_ANONYMIZE_IP` | `True` | If enabled, anonymizes IP addresses before storing them |
@@ -46,7 +46,8 @@ VENV_DIRNAME := ".venv"
46
46
 
47
47
  # run test suite
48
48
  @test: check_uv
49
- uv run pytest --cov --cov-report=html --cov-report=term -n auto
49
+ uv run python -m coverage run -m django test --settings tests.settings --parallel auto
50
+ uv run python -m coverage report
50
51
 
51
52
  # run test suite
52
53
  @test-all: check_uv
@@ -28,6 +28,7 @@ nav:
28
28
  - Home: "index.md"
29
29
  - Installation: "installation.md"
30
30
  - Settings: "settings.md"
31
+ - Models: "models.md"
31
32
  - Templates: "templates.md"
32
33
  - Contributing: "contributing.md"
33
34
  - Changelog: "changelog.md"
@@ -6,7 +6,7 @@ readme = "README.md"
6
6
  license = { file = "LICENSE" }
7
7
  keywords = ["django"]
8
8
  classifiers = [
9
- "Development Status :: 3 - Alpha",
9
+ "Development Status :: 4 - Beta",
10
10
  "Intended Audience :: Developers",
11
11
  "License :: OSI Approved :: MIT License",
12
12
  "Programming Language :: Python :: 3.10",
@@ -37,13 +37,9 @@ Repository = "https://codeberg.org/oliverandrich/django-solomon"
37
37
 
38
38
  [dependency-groups]
39
39
  dev = [
40
- "pytest-cov>=5.0.0",
41
- "pytest-django>=4.9.0",
42
- "pytest-mock>=3.14.0",
43
- "pytest-randomly>=3.15.0",
44
- "pytest>=8.3.3",
45
40
  "django-stubs[compatible-mypy]>=5.1.1",
46
- "pytest-xdist>=3.6.1",
41
+ "coverage[toml]",
42
+ "django-rich",
47
43
  ]
48
44
 
49
45
  [build-system]
@@ -1,7 +1,9 @@
1
1
  from django.contrib import admin
2
+ from django.contrib.auth.admin import UserAdmin
2
3
  from django.utils.translation import gettext_lazy as _
3
4
 
4
- from django_solomon.models import BlacklistedEmail, MagicLink
5
+ from django_solomon.forms import CustomUserCreationForm, CustomUserChangeForm
6
+ from django_solomon.models import BlacklistedEmail, MagicLink, CustomUser
5
7
 
6
8
 
7
9
  @admin.register(BlacklistedEmail)
@@ -43,3 +45,36 @@ class MagicLinkAdmin(admin.ModelAdmin):
43
45
  def is_valid(self, obj: MagicLink) -> bool:
44
46
  """Display if the magic link is valid."""
45
47
  return obj.is_valid
48
+
49
+
50
+ @admin.register(CustomUser)
51
+ class CustomUserAdmin(UserAdmin):
52
+ add_form = CustomUserCreationForm
53
+ form = CustomUserChangeForm
54
+ model = CustomUser
55
+ list_display = (
56
+ "email",
57
+ "is_superuser",
58
+ "is_staff",
59
+ "is_active",
60
+ )
61
+ list_filter = (
62
+ "is_superuser",
63
+ "is_staff",
64
+ "is_active",
65
+ )
66
+ fieldsets = (
67
+ (None, {"fields": ("email", "password")}),
68
+ ("Permissions", {"fields": ("is_staff", "is_active", "groups", "user_permissions")}),
69
+ )
70
+ add_fieldsets = (
71
+ (
72
+ None,
73
+ {
74
+ "classes": ("wide",),
75
+ "fields": ("email", "password1", "password2", "is_staff", "is_active", "groups", "user_permissions"),
76
+ },
77
+ ),
78
+ )
79
+ search_fields = ("email",)
80
+ ordering = ("email",)
@@ -0,0 +1,10 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoSolomonConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "django_solomon"
7
+
8
+ def ready(self):
9
+ # Import checks when app is ready
10
+ import django_solomon.checks # noqa
@@ -2,10 +2,11 @@ from typing import Any
2
2
 
3
3
  from django.conf import settings
4
4
  from django.contrib.auth.backends import ModelBackend
5
+ from django.contrib.auth.base_user import AbstractBaseUser
5
6
  from django.http import HttpRequest
6
7
  from django.utils.translation import gettext_lazy as _
7
8
 
8
- from django_solomon.models import MagicLink, UserType
9
+ from django_solomon.models import MagicLink
9
10
  from django_solomon.utilities import get_client_ip
10
11
 
11
12
 
@@ -15,7 +16,7 @@ class MagicLinkBackend(ModelBackend):
15
16
  request: HttpRequest | None = None,
16
17
  token: str | None = None,
17
18
  **kwargs: Any, # noqa: ARG002
18
- ) -> UserType | None:
19
+ ) -> AbstractBaseUser | None:
19
20
  """
20
21
  Authenticates a user based on the provided magic link token. This method validates
21
22
  the token, ensures the token is not expired, and checks if the user associated
@@ -0,0 +1,42 @@
1
+ # django_solomon/checks.py
2
+ from django.apps import apps
3
+ from django.conf import settings
4
+ from django.core.checks import Error, register
5
+
6
+
7
+ @register()
8
+ def check_user_model_email(app_configs, **kwargs): # noqa: ARG001
9
+ """
10
+ Check that the user model has a unique email field.
11
+ """
12
+ errors = []
13
+
14
+ # Get the user model
15
+ user_model = apps.get_model(settings.AUTH_USER_MODEL)
16
+
17
+ # Check if email field exists
18
+ try:
19
+ email_field = user_model._meta.get_field("email")
20
+ except Exception:
21
+ errors.append(
22
+ Error(
23
+ "User model does not have an email field.",
24
+ hint="Make sure your user model has an email field.",
25
+ obj=settings.AUTH_USER_MODEL,
26
+ id="django_solomon.E001",
27
+ )
28
+ )
29
+ return errors
30
+
31
+ # Check if email field is unique
32
+ if not email_field.unique:
33
+ errors.append(
34
+ Error(
35
+ "User model email field must be unique.",
36
+ hint="Set unique=True on the email field of your user model.",
37
+ obj=settings.AUTH_USER_MODEL,
38
+ id="django_solomon.E002",
39
+ )
40
+ )
41
+
42
+ return errors