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.
- {django_solomon-0.4.2 → django_solomon-0.5.0}/CHANGELOG.md +11 -15
- {django_solomon-0.4.2 → django_solomon-0.5.0}/PKG-INFO +16 -7
- {django_solomon-0.4.2 → django_solomon-0.5.0}/README.md +14 -5
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/installation.md +16 -4
- django_solomon-0.5.0/docs/models.md +145 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/settings.md +34 -23
- {django_solomon-0.4.2 → django_solomon-0.5.0}/justfile +2 -1
- {django_solomon-0.4.2 → django_solomon-0.5.0}/mkdocs.yml +1 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/pyproject.toml +3 -7
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/admin.py +36 -1
- django_solomon-0.5.0/src/django_solomon/apps.py +10 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/backends.py +3 -2
- django_solomon-0.5.0/src/django_solomon/checks.py +42 -0
- django_solomon-0.5.0/src/django_solomon/forms.py +27 -0
- django_solomon-0.5.0/src/django_solomon/migrations/0001_initial.py +73 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/models.py +56 -7
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/utilities.py +3 -4
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/settings.py +21 -0
- django_solomon-0.5.0/tests/test_admin.py +313 -0
- django_solomon-0.5.0/tests/test_backends.py +212 -0
- django_solomon-0.5.0/tests/test_checks.py +48 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/test_config.py +23 -21
- django_solomon-0.5.0/tests/test_forms.py +226 -0
- django_solomon-0.5.0/tests/test_models.py +271 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/test_utilities.py +72 -70
- django_solomon-0.5.0/tests/test_views.py +340 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tox.ini +2 -1
- {django_solomon-0.4.2 → django_solomon-0.5.0}/uv.lock +38 -122
- django_solomon-0.4.2/src/django_solomon/apps.py +0 -31
- django_solomon-0.4.2/src/django_solomon/forms.py +0 -12
- django_solomon-0.4.2/src/django_solomon/migrations/0001_initial.py +0 -47
- django_solomon-0.4.2/src/django_solomon/migrations/0002_magiclink_ip_address.py +0 -18
- django_solomon-0.4.2/src/django_solomon/migrations/0003_add_unique_constraint_auth_user_email.py +0 -43
- django_solomon-0.4.2/tests/conftest.py +0 -55
- django_solomon-0.4.2/tests/test_admin.py +0 -228
- django_solomon-0.4.2/tests/test_backends.py +0 -224
- django_solomon-0.4.2/tests/test_forms.py +0 -127
- django_solomon-0.4.2/tests/test_migrations.py +0 -77
- django_solomon-0.4.2/tests/test_models.py +0 -265
- django_solomon-0.4.2/tests/test_views.py +0 -305
- {django_solomon-0.4.2 → django_solomon-0.5.0}/.forgejo/workflows/release.yml +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/.forgejo/workflows/tests.yml +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/.gitignore +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/.pre-commit-config.yaml +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/.readthedocs.yml +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/LICENSE +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/changelog.md +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/contributing.md +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/index.md +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/requirements.txt +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/docs/templates.md +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/__init__.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/config.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/locale/de/LC_MESSAGES/django.po +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/locale/en/LC_MESSAGES/django.po +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/management/__init__.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/management/commands/__init__.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/migrations/__init__.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/py.typed +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/invalid_magic_link.html +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/login_form.html +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/base/magic_link_sent.html +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/email/magic_link.mjml +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/templates/django_solomon/email/magic_link.txt +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/urls.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/src/django_solomon/views.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/.gitignore +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/__init__.py +0 -0
- {django_solomon-0.4.2 → django_solomon-0.5.0}/tests/templates/base.html +0 -0
- {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.
|
8
|
+
## [0.5.0] - 2025-04-21
|
9
9
|
|
10
|
-
###
|
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
|
-
|
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
|
-
|
20
|
+
## [0.4.x] - 2025-04-20
|
25
21
|
|
26
|
-
-
|
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.
|
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 ::
|
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
|
-
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
###
|
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
|
-
###
|
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
|
-
###
|
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
|
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
|
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
|
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
|
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
|
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.,
|
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
|
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
|
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
|
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
|
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
|
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
|
@@ -6,7 +6,7 @@ readme = "README.md"
|
|
6
6
|
license = { file = "LICENSE" }
|
7
7
|
keywords = ["django"]
|
8
8
|
classifiers = [
|
9
|
-
"Development Status ::
|
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
|
-
"
|
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.
|
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",)
|
@@ -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
|
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
|
-
) ->
|
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
|