فهرست منبع

fix(api): limit Token.max * to prevent overflow in .is_valid

Peter Thomassen 2 سال پیش
والد
کامیت
3aad58a64d
2فایلهای تغییر یافته به همراه42 افزوده شده و 6 حذف شده
  1. 34 0
      api/desecapi/migrations/0025_alter_token_max_age_alter_token_max_unused_period.py
  2. 8 6
      api/desecapi/models.py

+ 34 - 0
api/desecapi/migrations/0025_alter_token_max_age_alter_token_max_unused_period.py

@@ -0,0 +1,34 @@
+# Generated by Django 4.0.6 on 2022-08-11 16:37
+
+import datetime
+import django.core.validators
+from django.db import migrations, models
+
+
+def forwards_func(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)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('desecapi', '0024_authenticatedchangeoutreachpreferenceuseraction'),
+    ]
+
+    operations = [
+        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(forwards_func, migrations.RunPython.noop),
+    ]

+ 8 - 6
api/desecapi/models.py

@@ -22,9 +22,9 @@ from django.contrib.auth.hashers import make_password
 from django.contrib.auth.models import AbstractBaseUser, AnonymousUser, BaseUserManager
 from django.contrib.auth.models import AbstractBaseUser, AnonymousUser, BaseUserManager
 from django.contrib.postgres.constraints import ExclusionConstraint
 from django.contrib.postgres.constraints import ExclusionConstraint
 from django.contrib.postgres.fields import ArrayField, CIEmailField, RangeOperators
 from django.contrib.postgres.fields import ArrayField, CIEmailField, RangeOperators
+from django.core import validators
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.core.mail import EmailMessage, get_connection
 from django.core.mail import EmailMessage, get_connection
-from django.core.validators import MinValueValidator, RegexValidator
 from django.db import models, transaction
 from django.db import models, transaction
 from django.db.models import CharField, F, Manager, Q, Value
 from django.db.models import CharField, F, Manager, Q, Value
 from django.db.models.expressions import RawSQL
 from django.db.models.expressions import RawSQL
@@ -199,7 +199,7 @@ class User(ExportModelOperationsMixin('User'), AbstractBaseUser):
 
 
 validate_domain_name = [
 validate_domain_name = [
     validate_lower,
     validate_lower,
-    RegexValidator(
+    validators.RegexValidator(
         regex=r'^(([a-z0-9_-]{1,63})\.)*[a-z0-9-]{1,63}$',
         regex=r'^(([a-z0-9_-]{1,63})\.)*[a-z0-9-]{1,63}$',
         message='Domain names must be labels separated by dots. Labels may consist of up to 63 letters, digits, '
         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.',
                 'hyphens, and underscores. The last label may not contain an underscore.',
@@ -424,6 +424,8 @@ class Token(ExportModelOperationsMixin('Token'), rest_framework.authtoken.models
     def _allowed_subnets_default():
     def _allowed_subnets_default():
         return [ipaddress.IPv4Network('0.0.0.0/0'), ipaddress.IPv6Network('::/0')]
         return [ipaddress.IPv4Network('0.0.0.0/0'), ipaddress.IPv6Network('::/0')]
 
 
+    _validators = [validators.MinValueValidator(timedelta(0)), validators.MaxValueValidator(timedelta(days=365*1000))]
+
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     key = models.CharField("Key", max_length=128, db_index=True, unique=True)
     key = models.CharField("Key", max_length=128, db_index=True, unique=True)
     user = models.ForeignKey(User, on_delete=models.CASCADE)
     user = models.ForeignKey(User, on_delete=models.CASCADE)
@@ -431,8 +433,8 @@ class Token(ExportModelOperationsMixin('Token'), rest_framework.authtoken.models
     last_used = models.DateTimeField(null=True, blank=True)
     last_used = models.DateTimeField(null=True, blank=True)
     perm_manage_tokens = models.BooleanField(default=False)
     perm_manage_tokens = models.BooleanField(default=False)
     allowed_subnets = ArrayField(CidrAddressField(), default=_allowed_subnets_default.__func__)
     allowed_subnets = ArrayField(CidrAddressField(), default=_allowed_subnets_default.__func__)
-    max_age = models.DurationField(null=True, default=None, validators=[MinValueValidator(timedelta(0))])
-    max_unused_period = models.DurationField(null=True, default=None, validators=[MinValueValidator(timedelta(0))])
+    max_age = models.DurationField(null=True, default=None, validators=_validators)
+    max_unused_period = models.DurationField(null=True, default=None, validators=_validators)
     domain_policies = models.ManyToManyField(Domain, through='TokenDomainPolicy')
     domain_policies = models.ManyToManyField(Domain, through='TokenDomainPolicy')
 
 
     plain = None
     plain = None
@@ -636,7 +638,7 @@ class RRset(ExportModelOperationsMixin('RRset'), models.Model):
         blank=True,
         blank=True,
         validators=[
         validators=[
             validate_lower,
             validate_lower,
-            RegexValidator(
+            validators.RegexValidator(
                 regex=r'^([*]|(([*][.])?([a-z0-9_-]{1,63}[.])*[a-z0-9_-]{1,63}))$',
                 regex=r'^([*]|(([*][.])?([a-z0-9_-]{1,63}[.])*[a-z0-9_-]{1,63}))$',
                 message='Subname can only use (lowercase) a-z, 0-9, ., -, and _, '
                 message='Subname can only use (lowercase) a-z, 0-9, ., -, and _, '
                         'may start with a \'*.\', or just be \'*\'. Components may not exceed 63 characters.',
                         'may start with a \'*.\', or just be \'*\'. Components may not exceed 63 characters.',
@@ -648,7 +650,7 @@ class RRset(ExportModelOperationsMixin('RRset'), models.Model):
         max_length=10,
         max_length=10,
         validators=[
         validators=[
             validate_upper,
             validate_upper,
-            RegexValidator(
+            validators.RegexValidator(
                 regex=r'^[A-Z][A-Z0-9]*$',
                 regex=r'^[A-Z][A-Z0-9]*$',
                 message='Type must be uppercase alphanumeric and start with a letter.',
                 message='Type must be uppercase alphanumeric and start with a letter.',
                 code='invalid_type'
                 code='invalid_type'