diff --git a/api/desecapi/migrations/0001_initial_squashed_again.py b/api/desecapi/migrations/0001_initial_squashed_again.py index dbb582bf8..4038d2d05 100644 --- a/api/desecapi/migrations/0001_initial_squashed_again.py +++ b/api/desecapi/migrations/0001_initial_squashed_again.py @@ -7,6 +7,7 @@ import django.db.models.deletion import re import uuid +from django.contrib.postgres.operations import CreateCollation class Migration(migrations.Migration): @@ -15,6 +16,13 @@ class Migration(migrations.Migration): dependencies = [] operations = [ + # Explanation: https://adamj.eu/tech/2023/02/23/migrate-django-postgresql-ci-fields-case-insensitive-collation/ + CreateCollation( + "case_insensitive", + provider="icu", + locale="und-u-ks-level2", + deterministic=False, + ), migrations.CreateModel( name="User", fields=[ @@ -37,7 +45,10 @@ class Migration(migrations.Migration): ( "email", models.EmailField( - max_length=191, unique=True, verbose_name="email address" + db_collation="case_insensitive", + max_length=254, + unique=True, + verbose_name="email address", ), ), ("is_active", models.BooleanField(default=True)), diff --git a/api/desecapi/migrations/0001_squashed_0031_alter_user_email.py b/api/desecapi/migrations/0001_squashed_0031_alter_user_email.py new file mode 100644 index 000000000..a8de890c3 --- /dev/null +++ b/api/desecapi/migrations/0001_squashed_0031_alter_user_email.py @@ -0,0 +1,1068 @@ +# Generated by Django 5.1 on 2024-08-30 13:27 + +import datetime +import desecapi.models.base +import desecapi.models.captcha +import desecapi.models.domains +import desecapi.models.donation +import desecapi.models.mfa +import desecapi.models.tokens +import desecapi.models.users +import django.contrib.postgres.constraints +import django.contrib.postgres.fields +import django.contrib.postgres.indexes +import django.contrib.postgres.operations +import django.core.validators +import django.db.migrations.operations.special +import django.db.models.deletion +import django.db.models.expressions +import netfields.fields +import re +import uuid +from django.conf import settings +from django.db import migrations, models +from django.db.models import F, Q + + +# Functions from the following migrations need manual copying. +# Move them and any dependencies into this file, then update the +# RunPython operations to refer to the local versions: +# desecapi.migrations.0007_email_citext +# desecapi.migrations.0019_alter_user_is_active +# desecapi.migrations.0020_user_email_verified +# desecapi.migrations.0025_alter_token_max_age_alter_token_max_unused_period +# desecapi.migrations.0027_user_credentials_changed +# desecapi.migrations.0031_alter_user_email + + +def alter_user_is_active_forward(apps, schema_editor): + User = apps.get_model("desecapi", "User") + db_alias = schema_editor.connection.alias + User.objects.using(db_alias).filter( + is_active=False, last_login__isnull=True + ).update(is_active=None) + + +def alter_user_is_active_reverse(apps, schema_editor): + User = apps.get_model("desecapi", "User") + db_alias = schema_editor.connection.alias + User.objects.using(db_alias).filter(is_active__isnull=True).update(is_active=False) + + +def user_email_verified_forward(apps, schema_editor): + User = apps.get_model("desecapi", "User") + db_alias = schema_editor.connection.alias + User.objects.using(db_alias).filter( + Q(is_active=True) | Q(last_login__isnull=False), + created__date__gte=datetime.date(2019, 11, 1), + ).update(email_verified=F("created")) + + +def alter_token_max_age_alter_token_max_unused_period_forward(apps, schema_editor): + max_interval = datetime.timedelta(days=365000) + Token = apps.get_model("desecapi", "Token") + db_alias = schema_editor.connection.alias + Token.objects.using(db_alias).filter(max_age__gt=max_interval).update( + max_age=max_interval + ) + Token.objects.using(db_alias).filter(max_unused_period__gt=max_interval).update( + max_unused_period=max_interval + ) + + +def user_credentials_changed_forward(apps, schema_editor): + User = apps.get_model("desecapi", "User") + db_alias = schema_editor.connection.alias + User.objects.using(db_alias).update(credentials_changed=F("created")) + + +class Migration(migrations.Migration): + + replaces = [ + ("desecapi", "0001_initial_squashed_again"), + ("desecapi", "0002_unmanaged_donations"), + ("desecapi", "0003_rr_content"), + ("desecapi", "0004_immortal_domains"), + ("desecapi", "0005_subname_validation"), + ("desecapi", "0006_cname_exclusivity"), + ("desecapi", "0007_email_citext"), + ("desecapi", "0008_token_perm_manage_tokens"), + ("desecapi", "0009_token_allowed_subnets"), + ("desecapi", "0010_token_expiration"), + ("desecapi", "0011_captcha_kind"), + ("desecapi", "0012_rrset_label_length"), + ("desecapi", "0013_user_needs_captcha"), + ("desecapi", "0014_replication"), + ("desecapi", "0015_rrset_touched_index"), + ("desecapi", "0016_default_auto_field"), + ("desecapi", "0017_alter_user_limit_domains"), + ("desecapi", "0018_tokendomainpolicy"), + ("desecapi", "0019_alter_user_is_active"), + ("desecapi", "0020_user_email_verified"), + ("desecapi", "0021_authenticatednoopuseraction"), + ("desecapi", "0022_user_outreach_preference"), + ("desecapi", "0023_authenticatedemailuseraction"), + ("desecapi", "0024_authenticatedchangeoutreachpreferenceuseraction"), + ("desecapi", "0025_alter_token_max_age_alter_token_max_unused_period"), + ("desecapi", "0026_remove_domain_replicated_and_more"), + ("desecapi", "0027_user_credentials_changed"), + ( + "desecapi", + "0028_authenticatedcreatetotpfactoruseraction_basefactor_and_more", + ), + ("desecapi", "0029_token_mfa"), + ("desecapi", "0030_blockedsubnet_blockedsubnet_subnet_idx"), + ("desecapi", "0031_alter_user_email"), + ] + + initial = True + + dependencies = [] + + operations = [ + django.contrib.postgres.operations.CreateCollation( + name="case_insensitive", + locale="und-u-ks-level2", + provider="icu", + deterministic=False, + ), + migrations.CreateModel( + name="User", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "email", + models.EmailField( + db_collation="case_insensitive", + max_length=254, + unique=True, + verbose_name="email address", + ), + ), + ("is_active", models.BooleanField(default=True)), + ("is_admin", models.BooleanField(default=False)), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "limit_domains", + models.IntegerField( + blank=True, + default=desecapi.models.users.User._limit_domains_default, + null=True, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Domain", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "name", + models.CharField( + max_length=191, + unique=True, + validators=[ + desecapi.models.base.validate_lower, + django.core.validators.RegexValidator( + code="invalid_domain_name", + flags=re.RegexFlag["IGNORECASE"], + message="Domain names must be labels separated by dots. Labels may consist of up to 63 letters, digits, hyphens, and underscores. The last label may not contain an underscore.", + regex="^(([a-z0-9_-]{1,63})\\.)*[a-z0-9-]{1,63}$", + ), + ], + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="domains", + to=settings.AUTH_USER_MODEL, + ), + ), + ("published", models.DateTimeField(blank=True, null=True)), + ( + "minimum_ttl", + models.PositiveIntegerField( + default=desecapi.models.domains.Domain._minimum_ttl_default + ), + ), + ("renewal_changed", models.DateTimeField(auto_now_add=True)), + ( + "renewal_state", + models.IntegerField( + choices=[ + (0, "Immortal"), + (1, "Fresh"), + (2, "Notified"), + (3, "Warned"), + ], + default=0, + ), + ), + ], + options={ + "ordering": ("created",), + }, + ), + migrations.CreateModel( + name="RRset", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("touched", models.DateTimeField(auto_now=True)), + ( + "subname", + models.CharField( + blank=True, + max_length=178, + validators=[ + desecapi.models.base.validate_lower, + django.core.validators.RegexValidator( + code="invalid_subname", + message="Subname can only use (lowercase) a-z, 0-9, ., -, and _, may start with a '*.', or just be '*'.", + regex="^([*]|(([*][.])?([a-z0-9_-]+[.])*[a-z0-9_-]+))$", + ), + ], + ), + ), + ( + "type", + models.CharField( + max_length=10, + validators=[ + desecapi.models.base.validate_upper, + django.core.validators.RegexValidator( + code="invalid_type", + message="Type must be uppercase alphanumeric and start with a letter.", + regex="^[A-Z][A-Z0-9]*$", + ), + ], + ), + ), + ("ttl", models.PositiveIntegerField()), + ( + "domain", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="desecapi.domain", + ), + ), + ], + options={ + "unique_together": {("domain", "subname", "type")}, + }, + ), + migrations.CreateModel( + name="AuthenticatedAction", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ], + options={ + "managed": False, + }, + ), + migrations.CreateModel( + name="AuthenticatedUserAction", + fields=[ + ( + "authenticatedaction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticatedaction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticatedaction",), + ), + migrations.CreateModel( + name="AuthenticatedDeleteUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.CreateModel( + name="AuthenticatedResetPasswordUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ("new_password", models.CharField(max_length=128)), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.CreateModel( + name="Captcha", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "content", + models.CharField( + default=desecapi.models.captcha.captcha_default_content, + max_length=24, + ), + ), + ], + ), + migrations.CreateModel( + name="Token", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "created", + models.DateTimeField(auto_now_add=True, verbose_name="Created"), + ), + ( + "key", + models.CharField( + db_index=True, max_length=128, unique=True, verbose_name="Key" + ), + ), + ( + "name", + models.CharField(blank=True, max_length=64, verbose_name="Name"), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="auth_tokens", + to=settings.AUTH_USER_MODEL, + verbose_name="User", + ), + ), + ("last_used", models.DateTimeField(blank=True, null=True)), + ], + options={ + "verbose_name": "Token", + "verbose_name_plural": "Tokens", + }, + ), + migrations.CreateModel( + name="AuthenticatedActivateUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ("domain", models.CharField(max_length=191)), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.CreateModel( + name="AuthenticatedChangeEmailUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ("new_email", models.EmailField(max_length=254)), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.CreateModel( + name="AuthenticatedBasicUserAction", + fields=[ + ( + "authenticatedaction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticatedaction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticatedaction",), + ), + migrations.CreateModel( + name="AuthenticatedDomainBasicUserAction", + fields=[ + ( + "authenticatedbasicuseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticatedbasicuseraction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticatedbasicuseraction",), + ), + migrations.CreateModel( + name="AuthenticatedRenewDomainBasicUserAction", + fields=[ + ( + "authenticateddomainbasicuseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateddomainbasicuseraction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateddomainbasicuseraction",), + ), + migrations.CreateModel( + name="Donation", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created", + models.DateTimeField( + default=desecapi.models.donation.Donation._created_default + ), + ), + ("name", models.CharField(max_length=255)), + ("iban", models.CharField(max_length=34)), + ("bic", models.CharField(max_length=11)), + ("amount", models.DecimalField(decimal_places=2, max_digits=8)), + ("message", models.CharField(blank=True, max_length=255)), + ( + "due", + models.DateTimeField( + default=desecapi.models.donation.Donation._due_default + ), + ), + ( + "mref", + models.CharField( + default=desecapi.models.donation.Donation._mref_default, + max_length=32, + ), + ), + ("email", models.EmailField(blank=True, max_length=255)), + ], + options={ + "managed": False, + }, + ), + migrations.CreateModel( + name="RR", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("content", models.TextField()), + ( + "rrset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="records", + to="desecapi.rrset", + ), + ), + ], + ), + django.contrib.postgres.operations.BtreeGistExtension(), + migrations.AddConstraint( + model_name="rrset", + constraint=django.contrib.postgres.constraints.ExclusionConstraint( + expressions=[ + ("domain", "="), + ("subname", "="), + ( + django.db.models.expressions.RawSQL("int4(type = 'CNAME')", ()), + "<>", + ), + ], + name="cname_exclusivity", + ), + ), + migrations.AddField( + model_name="token", + name="perm_manage_tokens", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="token", + name="allowed_subnets", + field=django.contrib.postgres.fields.ArrayField( + base_field=netfields.fields.CidrAddressField(max_length=43), + default=desecapi.models.tokens.Token._allowed_subnets_default, + size=None, + ), + ), + migrations.AddField( + model_name="token", + name="max_age", + field=models.DurationField( + default=None, + null=True, + validators=[ + django.core.validators.MinValueValidator(datetime.timedelta(0)) + ], + ), + ), + migrations.AddField( + model_name="token", + name="max_unused_period", + field=models.DurationField( + default=None, + null=True, + validators=[ + django.core.validators.MinValueValidator(datetime.timedelta(0)) + ], + ), + ), + migrations.AddField( + model_name="captcha", + name="kind", + field=models.CharField( + choices=[("image", "Image"), ("audio", "Audio")], + default="image", + max_length=24, + ), + ), + migrations.AlterField( + model_name="captcha", + name="content", + field=models.CharField(default="", max_length=24), + ), + migrations.AlterField( + model_name="rrset", + name="subname", + field=models.CharField( + blank=True, + max_length=178, + validators=[ + desecapi.models.base.validate_lower, + django.core.validators.RegexValidator( + code="invalid_subname", + message="Subname can only use (lowercase) a-z, 0-9, ., -, and _, may start with a '*.', or just be '*'. Components may not exceed 63 characters.", + regex="^([*]|(([*][.])?([a-z0-9_-]{1,63}[.])*[a-z0-9_-]{1,63}))$", + ), + ], + ), + ), + migrations.AddField( + model_name="user", + name="needs_captcha", + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name="domain", + name="replicated", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="domain", + name="replication_duration", + field=models.DurationField(blank=True, null=True), + ), + migrations.AlterField( + model_name="rrset", + name="touched", + field=models.DateTimeField(auto_now=True, db_index=True), + ), + migrations.AlterField( + model_name="domain", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="rr", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="user", + name="limit_domains", + field=models.PositiveIntegerField( + blank=True, + default=desecapi.models.users.User._limit_domains_default, + null=True, + ), + ), + migrations.AlterField( + model_name="token", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.CreateModel( + name="TokenDomainPolicy", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("perm_dyndns", models.BooleanField(default=False)), + ("perm_rrsets", models.BooleanField(default=False)), + ( + "domain", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="desecapi.domain", + ), + ), + ( + "token", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="desecapi.token" + ), + ), + ( + "token_user", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddField( + model_name="token", + name="domain_policies", + field=models.ManyToManyField( + through="desecapi.TokenDomainPolicy", to="desecapi.domain" + ), + ), + migrations.AddConstraint( + model_name="tokendomainpolicy", + constraint=models.UniqueConstraint( + fields=("token", "domain"), name="unique_entry" + ), + ), + migrations.AddConstraint( + model_name="tokendomainpolicy", + constraint=models.UniqueConstraint( + condition=models.Q(("domain__isnull", True)), + fields=("token",), + name="unique_entry_null_domain", + ), + ), + migrations.AlterModelOptions( + name="token", + options={}, + ), + migrations.AddConstraint( + model_name="token", + constraint=models.UniqueConstraint( + fields=("id", "user"), name="unique_id_user" + ), + ), + migrations.AddConstraint( + model_name="domain", + constraint=models.UniqueConstraint( + fields=("id", "owner"), name="unique_id_owner" + ), + ), + migrations.RunSQL( + sql="ALTER TABLE desecapi_tokendomainpolicy ADD FOREIGN KEY ( domain_id, token_user_id ) REFERENCES desecapi_domain ( id, owner_id ), ADD FOREIGN KEY ( token_id, token_user_id ) REFERENCES desecapi_token ( id, user_id );", + reverse_sql="", + ), + migrations.AlterField( + model_name="user", + name="is_active", + field=models.BooleanField(default=True, null=True), + ), + migrations.RunPython( + code=alter_user_is_active_forward, + reverse_code=alter_user_is_active_reverse, + ), + migrations.AddField( + model_name="user", + name="email_verified", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.RunPython( + code=user_email_verified_forward, + reverse_code=django.db.migrations.operations.special.RunPython.noop, + ), + migrations.CreateModel( + name="AuthenticatedNoopUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.AddField( + model_name="user", + name="outreach_preference", + field=models.BooleanField(default=True), + ), + migrations.CreateModel( + name="AuthenticatedEmailUserAction", + fields=[ + ( + "authenticatedbasicuseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticatedbasicuseraction", + ), + ), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticatedbasicuseraction",), + ), + migrations.CreateModel( + name="AuthenticatedChangeOutreachPreferenceUserAction", + fields=[ + ( + "authenticatedemailuseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticatedemailuseraction", + ), + ), + ("outreach_preference", models.BooleanField()), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticatedemailuseraction",), + ), + migrations.AlterField( + model_name="token", + name="max_age", + field=models.DurationField( + default=None, + null=True, + validators=[ + django.core.validators.MinValueValidator(datetime.timedelta(0)), + django.core.validators.MaxValueValidator( + datetime.timedelta(days=365000) + ), + ], + ), + ), + migrations.AlterField( + model_name="token", + name="max_unused_period", + field=models.DurationField( + default=None, + null=True, + validators=[ + django.core.validators.MinValueValidator(datetime.timedelta(0)), + django.core.validators.MaxValueValidator( + datetime.timedelta(days=365000) + ), + ], + ), + ), + migrations.RunPython( + code=alter_token_max_age_alter_token_max_unused_period_forward, + reverse_code=django.db.migrations.operations.special.RunPython.noop, + ), + migrations.RemoveField( + model_name="domain", + name="replicated", + ), + migrations.RemoveField( + model_name="domain", + name="replication_duration", + ), + migrations.AddField( + model_name="user", + name="credentials_changed", + field=models.DateTimeField(auto_now_add=True, null=True), + ), + migrations.RunPython( + code=user_credentials_changed_forward, + reverse_code=django.db.migrations.operations.special.RunPython.noop, + ), + migrations.AlterField( + model_name="user", + name="credentials_changed", + field=models.DateTimeField(auto_now_add=True), + ), + migrations.CreateModel( + name="AuthenticatedCreateTOTPFactorUserAction", + fields=[ + ( + "authenticateduseraction_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.authenticateduseraction", + ), + ), + ("name", models.CharField(blank=True, max_length=64)), + ], + options={ + "managed": False, + }, + bases=("desecapi.authenticateduseraction",), + ), + migrations.CreateModel( + name="BaseFactor", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("last_used", models.DateTimeField(blank=True, null=True)), + ("name", models.CharField(blank=True, default="", max_length=64)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.CreateModel( + name="TOTPFactor", + fields=[ + ( + "basefactor_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="desecapi.basefactor", + ), + ), + ( + "secret", + models.BinaryField( + default=desecapi.models.mfa.TOTPFactor._secret_default, + max_length=32, + ), + ), + ("last_verified_timestep", models.PositiveIntegerField(default=0)), + ], + bases=("desecapi.basefactor",), + ), + migrations.AddConstraint( + model_name="basefactor", + constraint=models.UniqueConstraint( + fields=("user", "name"), name="unique_user_name" + ), + ), + migrations.AddField( + model_name="token", + name="mfa", + field=models.BooleanField(default=None, null=True), + ), + migrations.CreateModel( + name="BlockedSubnet", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "asn", + models.PositiveBigIntegerField( + validators=[ + django.core.validators.MaxValueValidator(4294967295) + ] + ), + ), + ( + "subnet", + netfields.fields.CidrAddressField(max_length=43, unique=True), + ), + ("country", models.TextField()), + ("registry", models.TextField()), + ("allocation_date", models.DateField()), + ], + options={ + "indexes": [ + django.contrib.postgres.indexes.GistIndex( + fields=["subnet"], name="subnet_idx", opclasses=("inet_ops",) + ) + ], + }, + ), + ] diff --git a/api/desecapi/migrations/0007_email_citext.py b/api/desecapi/migrations/0007_email_citext.py index aac2b23fe..dc4c26ba1 100644 --- a/api/desecapi/migrations/0007_email_citext.py +++ b/api/desecapi/migrations/0007_email_citext.py @@ -1,7 +1,7 @@ # Generated by Django 3.1 on 2020-09-29 14:28 -import django.contrib.postgres.fields.citext -from django.contrib.postgres.operations import CITextExtension +# import django.contrib.postgres.fields.citext +# from django.contrib.postgres.operations import CITextExtension from django.db import migrations @@ -11,12 +11,12 @@ class Migration(migrations.Migration): ] operations = [ - CITextExtension(), - migrations.AlterField( - model_name="user", - name="email", - field=django.contrib.postgres.fields.citext.CIEmailField( - max_length=254, unique=True, verbose_name="email address" - ), - ), + # CITextExtension(), + # migrations.AlterField( + # model_name="user", + # name="email", + # field=django.contrib.postgres.fields.citext.CIEmailField( + # max_length=254, unique=True, verbose_name="email address" + # ), + # ), ] diff --git a/api/desecapi/migrations/0031_alter_user_email.py b/api/desecapi/migrations/0031_alter_user_email.py index e4ba9e896..5ddd31239 100644 --- a/api/desecapi/migrations/0031_alter_user_email.py +++ b/api/desecapi/migrations/0031_alter_user_email.py @@ -1,7 +1,7 @@ # Generated by Django 4.1.9 on 2023-06-08 16:40 -from django.contrib.postgres.operations import CreateCollation -from django.db import migrations, models +# from django.contrib.postgres.operations import CreateCollation +from django.db import migrations # , models class Migration(migrations.Migration): @@ -10,23 +10,23 @@ class Migration(migrations.Migration): ] operations = [ - # Explanation: https://adamj.eu/tech/2023/02/23/migrate-django-postgresql-ci-fields-case-insensitive-collation/ - CreateCollation( - "case_insensitive", - provider="icu", - locale="und-u-ks-level2", - deterministic=False, - ), - migrations.AlterField( - model_name="user", - name="email", - field=models.EmailField( - db_collation="case_insensitive", - max_length=254, - unique=True, - verbose_name="email address", - ), - ), + # # Explanation: https://adamj.eu/tech/2023/02/23/migrate-django-postgresql-ci-fields-case-insensitive-collation/ + # CreateCollation( + # "case_insensitive", + # provider="icu", + # locale="und-u-ks-level2", + # deterministic=False, + # ), + # migrations.AlterField( + # model_name="user", + # name="email", + # field=models.EmailField( + # db_collation="case_insensitive", + # max_length=254, + # unique=True, + # verbose_name="email address", + # ), + # ), migrations.RunSQL( sql='DROP EXTENSION IF EXISTS "citext"', reverse_sql='CREATE EXTENSION IF NOT EXISTS "citext"',