django-solomon 0.4.3__py3-none-any.whl → 0.5.0__py3-none-any.whl
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/admin.py +36 -1
- django_solomon/apps.py +2 -22
- django_solomon/backends.py +3 -2
- django_solomon/checks.py +42 -0
- django_solomon/forms.py +15 -0
- django_solomon/migrations/0001_initial.py +28 -2
- django_solomon/models.py +56 -7
- django_solomon/utilities.py +3 -4
- {django_solomon-0.4.3.dist-info → django_solomon-0.5.0.dist-info}/METADATA +16 -8
- {django_solomon-0.4.3.dist-info → django_solomon-0.5.0.dist-info}/RECORD +12 -13
- django_solomon/migrations/0002_magiclink_ip_address.py +0 -18
- django_solomon/migrations/0003_add_unique_constraint_auth_user_email.py +0 -42
- {django_solomon-0.4.3.dist-info → django_solomon-0.5.0.dist-info}/WHEEL +0 -0
- {django_solomon-0.4.3.dist-info → django_solomon-0.5.0.dist-info}/licenses/LICENSE +0 -0
django_solomon/admin.py
CHANGED
@@ -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",)
|
django_solomon/apps.py
CHANGED
@@ -6,25 +6,5 @@ class DjangoSolomonConfig(AppConfig):
|
|
6
6
|
name = "django_solomon"
|
7
7
|
|
8
8
|
def ready(self):
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# https://github.com/carltongibson/django-unique-user-email/
|
12
|
-
|
13
|
-
from django.contrib.auth.models import User
|
14
|
-
from django.db import models
|
15
|
-
|
16
|
-
# Updating the field itself triggers the auto-detector
|
17
|
-
# field = User._meta.get_field("email")
|
18
|
-
# field._unique = True
|
19
|
-
#
|
20
|
-
# But setting a constraint does not...
|
21
|
-
User.Meta.constraints = [
|
22
|
-
models.UniqueConstraint(
|
23
|
-
fields=["email"],
|
24
|
-
name="unique_user_email",
|
25
|
-
# deferrable=models.Deferrable.DEFERRED,
|
26
|
-
),
|
27
|
-
]
|
28
|
-
User._meta.constraints = User.Meta.constraints
|
29
|
-
# ... as long as original_attrs is not updated.
|
30
|
-
# User._meta.original_attrs["constraints"] = User.Meta.constraints
|
9
|
+
# Import checks when app is ready
|
10
|
+
import django_solomon.checks # noqa
|
django_solomon/backends.py
CHANGED
@@ -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
|
django_solomon/checks.py
ADDED
@@ -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
|
django_solomon/forms.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
from django import forms
|
2
|
+
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
|
2
3
|
from django.utils.translation import gettext_lazy as _
|
3
4
|
|
5
|
+
from django_solomon.models import CustomUser
|
6
|
+
|
4
7
|
|
5
8
|
class MagicLinkForm(forms.Form):
|
6
9
|
"""Form for requesting a magic link."""
|
@@ -10,3 +13,15 @@ class MagicLinkForm(forms.Form):
|
|
10
13
|
max_length=254,
|
11
14
|
widget=forms.EmailInput(attrs={"autocomplete": "email"}),
|
12
15
|
)
|
16
|
+
|
17
|
+
|
18
|
+
class CustomUserCreationForm(UserCreationForm):
|
19
|
+
class Meta:
|
20
|
+
model = CustomUser
|
21
|
+
fields = ("email",)
|
22
|
+
|
23
|
+
|
24
|
+
class CustomUserChangeForm(UserChangeForm):
|
25
|
+
class Meta:
|
26
|
+
model = CustomUser
|
27
|
+
fields = ("email",)
|
@@ -1,6 +1,8 @@
|
|
1
|
-
# Generated by Django 5.2 on 2025-04-
|
1
|
+
# Generated by Django 5.2 on 2025-04-21 11:50
|
2
2
|
|
3
|
+
import django.contrib.auth.validators
|
3
4
|
import django.db.models.deletion
|
5
|
+
import django.utils.timezone
|
4
6
|
from django.conf import settings
|
5
7
|
from django.db import migrations, models
|
6
8
|
|
@@ -10,7 +12,7 @@ class Migration(migrations.Migration):
|
|
10
12
|
initial = True
|
11
13
|
|
12
14
|
dependencies = [
|
13
|
-
|
15
|
+
('auth', '0012_alter_user_first_name_max_length'),
|
14
16
|
]
|
15
17
|
|
16
18
|
operations = [
|
@@ -28,6 +30,29 @@ class Migration(migrations.Migration):
|
|
28
30
|
'ordering': ['-created_at'],
|
29
31
|
},
|
30
32
|
),
|
33
|
+
migrations.CreateModel(
|
34
|
+
name='CustomUser',
|
35
|
+
fields=[
|
36
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
37
|
+
('password', models.CharField(max_length=128, verbose_name='password')),
|
38
|
+
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
39
|
+
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
40
|
+
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
41
|
+
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
42
|
+
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
43
|
+
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
44
|
+
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
45
|
+
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
46
|
+
('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
|
47
|
+
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
48
|
+
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
49
|
+
],
|
50
|
+
options={
|
51
|
+
'verbose_name': 'user',
|
52
|
+
'verbose_name_plural': 'users',
|
53
|
+
'abstract': False,
|
54
|
+
},
|
55
|
+
),
|
31
56
|
migrations.CreateModel(
|
32
57
|
name='MagicLink',
|
33
58
|
fields=[
|
@@ -36,6 +61,7 @@ class Migration(migrations.Migration):
|
|
36
61
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
|
37
62
|
('expires_at', models.DateTimeField(verbose_name='Expires at')),
|
38
63
|
('used', models.BooleanField(default=False, verbose_name='Used')),
|
64
|
+
('ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP Address')),
|
39
65
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='magic_links', to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
40
66
|
],
|
41
67
|
options={
|
django_solomon/models.py
CHANGED
@@ -1,16 +1,63 @@
|
|
1
|
+
import uuid
|
1
2
|
from datetime import timedelta
|
2
|
-
from typing import Any
|
3
|
+
from typing import Any
|
3
4
|
|
4
5
|
from django.conf import settings
|
5
|
-
from django.contrib.auth import
|
6
|
-
from django.contrib.auth.
|
6
|
+
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
|
7
|
+
from django.contrib.auth.models import AbstractUser
|
7
8
|
from django.contrib.auth.tokens import default_token_generator
|
8
9
|
from django.db import models
|
9
10
|
from django.utils import timezone
|
10
11
|
from django.utils.translation import gettext_lazy as _
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
|
14
|
+
class CustomUserManager(BaseUserManager):
|
15
|
+
"""
|
16
|
+
Custom user model manager where email is the unique identifiers
|
17
|
+
for authentication instead of usernames.
|
18
|
+
"""
|
19
|
+
|
20
|
+
def create_user(self, email, password, **extra_fields):
|
21
|
+
"""
|
22
|
+
Create and save a user with the given email and password.
|
23
|
+
"""
|
24
|
+
if not email:
|
25
|
+
raise ValueError(_("The Email must be set"))
|
26
|
+
email = self.normalize_email(email)
|
27
|
+
user = self.model(email=email, **extra_fields)
|
28
|
+
user.set_password(password)
|
29
|
+
user.save()
|
30
|
+
return user
|
31
|
+
|
32
|
+
def create_superuser(self, email, password, **extra_fields):
|
33
|
+
"""
|
34
|
+
Create and save a SuperUser with the given email and password.
|
35
|
+
"""
|
36
|
+
extra_fields.setdefault("is_staff", True)
|
37
|
+
extra_fields.setdefault("is_superuser", True)
|
38
|
+
extra_fields.setdefault("is_active", True)
|
39
|
+
|
40
|
+
if extra_fields.get("is_staff") is not True:
|
41
|
+
raise ValueError(_("Superuser must have is_staff=True."))
|
42
|
+
if extra_fields.get("is_superuser") is not True:
|
43
|
+
raise ValueError(_("Superuser must have is_superuser=True."))
|
44
|
+
return self.create_user(email, password, **extra_fields)
|
45
|
+
|
46
|
+
|
47
|
+
class CustomUser(AbstractUser):
|
48
|
+
email = models.EmailField(_("email address"), unique=True)
|
49
|
+
|
50
|
+
USERNAME_FIELD = "email"
|
51
|
+
REQUIRED_FIELDS = ["username"]
|
52
|
+
|
53
|
+
objects = CustomUserManager()
|
54
|
+
|
55
|
+
def __str__(self):
|
56
|
+
return self.email
|
57
|
+
|
58
|
+
def save(self, *args, **kwargs):
|
59
|
+
self.username = self.email
|
60
|
+
super().save(*args, **kwargs)
|
14
61
|
|
15
62
|
|
16
63
|
class BlacklistedEmail(models.Model):
|
@@ -42,7 +89,7 @@ class MagicLinkManager(models.Manager["MagicLink"]):
|
|
42
89
|
criteria like token, usage status, and expiration date.
|
43
90
|
"""
|
44
91
|
|
45
|
-
def create_for_user(self, user:
|
92
|
+
def create_for_user(self, user: AbstractBaseUser, ip_address: str = None) -> "MagicLink":
|
46
93
|
"""
|
47
94
|
Creates a new magic link for a given user. If the setting SOLOMON_ONLY_ONE_LINK_ALLOWED
|
48
95
|
is enabled, marks existing links for the user as used before creating a new one.
|
@@ -110,7 +157,9 @@ class MagicLink(models.Model):
|
|
110
157
|
based on a predefined duration from the settings.
|
111
158
|
"""
|
112
159
|
if not self.pk or not self.token:
|
113
|
-
|
160
|
+
base_token = default_token_generator.make_token(self.user)
|
161
|
+
unique_component = uuid.uuid4().hex[:8] # 8 characters from UUID
|
162
|
+
self.token = f"{base_token}-{unique_component}"
|
114
163
|
|
115
164
|
if not self.expires_at:
|
116
165
|
expiration_time = getattr(settings, "SOLOMON_LINK_EXPIRATION", 300) # seconds
|
django_solomon/utilities.py
CHANGED
@@ -4,14 +4,13 @@ import socket
|
|
4
4
|
|
5
5
|
from django.conf import settings
|
6
6
|
from django.contrib.auth import get_user_model
|
7
|
+
from django.contrib.auth.base_user import AbstractBaseUser
|
7
8
|
from django.contrib.auth.hashers import make_password
|
8
9
|
|
9
|
-
from django_solomon.models import UserType
|
10
|
-
|
11
10
|
logger = logging.getLogger(__name__)
|
12
11
|
|
13
12
|
|
14
|
-
def get_user_by_email(email: str) ->
|
13
|
+
def get_user_by_email(email: str) -> AbstractBaseUser | None:
|
15
14
|
"""
|
16
15
|
Get a user by email.
|
17
16
|
|
@@ -53,7 +52,7 @@ def get_user_by_email(email: str) -> UserType | None:
|
|
53
52
|
return None
|
54
53
|
|
55
54
|
|
56
|
-
def create_user_from_email(email: str) ->
|
55
|
+
def create_user_from_email(email: str) -> AbstractBaseUser:
|
57
56
|
"""
|
58
57
|
Creates a new user with the given email.
|
59
58
|
|
@@ -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,8 +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
|
-
from [django-unique-user-email](https://github.com/carltongibson/django-unique-user-email). Thanks!)
|
76
|
+
- System checks to ensure proper configuration
|
77
77
|
|
78
78
|
## Installation
|
79
79
|
|
@@ -93,7 +93,15 @@ INSTALLED_APPS = [
|
|
93
93
|
]
|
94
94
|
```
|
95
95
|
|
96
|
-
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:
|
97
105
|
|
98
106
|
```python
|
99
107
|
AUTHENTICATION_BACKENDS = [
|
@@ -102,7 +110,7 @@ AUTHENTICATION_BACKENDS = [
|
|
102
110
|
]
|
103
111
|
```
|
104
112
|
|
105
|
-
|
113
|
+
4. Include the django-solomon URLs in your project's `urls.py`:
|
106
114
|
|
107
115
|
```python
|
108
116
|
from django.urls import include, path
|
@@ -114,7 +122,7 @@ urlpatterns = [
|
|
114
122
|
]
|
115
123
|
```
|
116
124
|
|
117
|
-
|
125
|
+
5. Set the login URL in your settings to use django-solomon's login view:
|
118
126
|
|
119
127
|
```python
|
120
128
|
LOGIN_URL = 'django_solomon:login'
|
@@ -122,7 +130,7 @@ LOGIN_URL = 'django_solomon:login'
|
|
122
130
|
|
123
131
|
This ensures that when users need to authenticate, they'll be redirected to the magic link login page.
|
124
132
|
|
125
|
-
|
133
|
+
6. Configure your email settings to ensure emails can be sent:
|
126
134
|
|
127
135
|
```python
|
128
136
|
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
@@ -1,28 +1,27 @@
|
|
1
1
|
django_solomon/__init__.py,sha256=4GdjeQVyChzdc7pZ1jrpknjcnu9Do_0nSh42UZNICKo,80
|
2
|
-
django_solomon/admin.py,sha256=
|
3
|
-
django_solomon/apps.py,sha256=
|
4
|
-
django_solomon/backends.py,sha256=
|
2
|
+
django_solomon/admin.py,sha256=H1q4qXN0uccLY3OFt7v9FisnLwANStDIr5JWMP8gByw,2401
|
3
|
+
django_solomon/apps.py,sha256=Ln0Nbv7ZQTb25l4p2CXyxtKFqy60Q9AEBCXIapYaDLk,268
|
4
|
+
django_solomon/backends.py,sha256=ruv8Lop5js4MQmTQV0cl07Hc11sZ1Kty6QOS5J92ZbI,3228
|
5
|
+
django_solomon/checks.py,sha256=yF2IFogiUreaAQcw2YzTUtq1mkd9hpzvdgbh3u0oopY,1198
|
5
6
|
django_solomon/config.py,sha256=JEl3cY0BnfC9ja0ZVeLmfIwUStbUAO6vRhGZrrig5Yw,787
|
6
|
-
django_solomon/forms.py,sha256=
|
7
|
-
django_solomon/models.py,sha256=
|
7
|
+
django_solomon/forms.py,sha256=Y6PsFbeSNLaCB3gfWE4ekvg6sJmtyy9DGn35ZJ5Qejo,667
|
8
|
+
django_solomon/models.py,sha256=9npDBTcCvlFbi4wSfV7IEU3WDgoincjMDmwPQqGU8lU,6567
|
8
9
|
django_solomon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
10
|
django_solomon/urls.py,sha256=tT-I-p6dA2rD1IvH3pAcJRkPXYW0oYmYWXZAoiLJzGY,471
|
10
|
-
django_solomon/utilities.py,sha256=
|
11
|
+
django_solomon/utilities.py,sha256=FpWFqkPf4tzjItjuTrVfKRhjZw5449VZ7Y1gjPrcuhQ,4590
|
11
12
|
django_solomon/views.py,sha256=gqsj1SVd2bbQvla3xpZYVHYCUxhu3fr_5I-LY-Tvm2g,5737
|
12
13
|
django_solomon/locale/de/LC_MESSAGES/django.po,sha256=6lo72lsDV1bKBVowiRsQoaVKH5Mbkf34z78sG1LEYq4,747
|
13
14
|
django_solomon/locale/en/LC_MESSAGES/django.po,sha256=6MjGCWQn-1AjOnP_gXtLa0Oad4qfNAn5tXdhnw_wEcg,732
|
14
15
|
django_solomon/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
16
|
django_solomon/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
django_solomon/migrations/0001_initial.py,sha256=
|
17
|
-
django_solomon/migrations/0002_magiclink_ip_address.py,sha256=hs3MISYV7IHEqF-vELCmFtzhhvK8GEHkZu8RMc4X1YU,432
|
18
|
-
django_solomon/migrations/0003_add_unique_constraint_auth_user_email.py,sha256=2eZm-oSTtf9zmEy4fJYE6fjMAsn_VUS_zKfW3WQNGoQ,1487
|
17
|
+
django_solomon/migrations/0001_initial.py,sha256=RlyI1VV2kDXcGS3waQ1ujieq8gGx66YU75phWhwxTlU,4558
|
19
18
|
django_solomon/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
19
|
django_solomon/templates/django_solomon/base/invalid_magic_link.html,sha256=3U4T2n4uvPvktiOOHYVyNmk0aXFfE4DPrURE1DinWKk,554
|
21
20
|
django_solomon/templates/django_solomon/base/login_form.html,sha256=VYGnrno3c36W9f04XdRqJpjsDfOy0sq5D_cLayPz8Q0,546
|
22
21
|
django_solomon/templates/django_solomon/base/magic_link_sent.html,sha256=GIMxnw3E98TXVkVQkMRTHmX5mz0xUsvgXVj25gO2HPQ,461
|
23
22
|
django_solomon/templates/django_solomon/email/magic_link.mjml,sha256=OnfVGm2yFrOmoQ1syo6-Duq_Qraf3Tv0Vy5oidvt55g,1427
|
24
23
|
django_solomon/templates/django_solomon/email/magic_link.txt,sha256=yl01eie3U2HkFEJvB1Qlm8Z6FPuop5yubDXFY5V507Q,502
|
25
|
-
django_solomon-0.
|
26
|
-
django_solomon-0.
|
27
|
-
django_solomon-0.
|
28
|
-
django_solomon-0.
|
24
|
+
django_solomon-0.5.0.dist-info/METADATA,sha256=KmHBIVEGRYWuemsWRm4YLBEA2Wx-Wn2w1DCNoWzWnUQ,10792
|
25
|
+
django_solomon-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
26
|
+
django_solomon-0.5.0.dist-info/licenses/LICENSE,sha256=tbfFOFdH5mHIfmNGo6KGOC3U8f-hTCLfetcr8A89WwI,1071
|
27
|
+
django_solomon-0.5.0.dist-info/RECORD,,
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# Generated by Django 5.2 on 2025-04-20 14:59
|
2
|
-
|
3
|
-
from django.db import migrations, models
|
4
|
-
|
5
|
-
|
6
|
-
class Migration(migrations.Migration):
|
7
|
-
|
8
|
-
dependencies = [
|
9
|
-
('django_solomon', '0001_initial'),
|
10
|
-
]
|
11
|
-
|
12
|
-
operations = [
|
13
|
-
migrations.AddField(
|
14
|
-
model_name='magiclink',
|
15
|
-
name='ip_address',
|
16
|
-
field=models.GenericIPAddressField(blank=True, null=True, verbose_name='IP Address'),
|
17
|
-
),
|
18
|
-
]
|
@@ -1,42 +0,0 @@
|
|
1
|
-
from django.db import migrations, models
|
2
|
-
|
3
|
-
|
4
|
-
# Copyright (c) 2023 Carlton Gibson
|
5
|
-
# This migration is taken from the fantastic project `django-unique-user-email` created by Carlton Gibson.
|
6
|
-
# https://github.com/carltongibson/django-unique-user-email/
|
7
|
-
|
8
|
-
|
9
|
-
class CustomAddConstraint(migrations.AddConstraint):
|
10
|
-
"""
|
11
|
-
Override app_label to target auth.User
|
12
|
-
"""
|
13
|
-
|
14
|
-
def state_forwards(self, app_label, state):
|
15
|
-
state.add_constraint("auth", self.model_name_lower, self.constraint)
|
16
|
-
|
17
|
-
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
18
|
-
model = to_state.apps.get_model("auth", self.model_name)
|
19
|
-
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
20
|
-
schema_editor.add_constraint(model, self.constraint)
|
21
|
-
|
22
|
-
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
23
|
-
model = to_state.apps.get_model("auth", self.model_name)
|
24
|
-
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
25
|
-
schema_editor.remove_constraint(model, self.constraint)
|
26
|
-
|
27
|
-
|
28
|
-
class Migration(migrations.Migration):
|
29
|
-
dependencies = [
|
30
|
-
("django_solomon", "0002_magiclink_ip_address"),
|
31
|
-
("auth", "0012_alter_user_first_name_max_length"),
|
32
|
-
]
|
33
|
-
|
34
|
-
operations = [
|
35
|
-
CustomAddConstraint(
|
36
|
-
model_name="user",
|
37
|
-
constraint=models.UniqueConstraint(
|
38
|
-
fields=["email"],
|
39
|
-
name="unique_user_email",
|
40
|
-
),
|
41
|
-
),
|
42
|
-
]
|
File without changes
|
File without changes
|