diff --git a/api/api/settings.py b/api/api/settings.py
index c073e3d63..5dd0d44ac 100644
--- a/api/api/settings.py
+++ b/api/api/settings.py
@@ -224,9 +224,7 @@
VALIDITY_PERIOD_VERIFICATION_SIGNATURE = timedelta(
hours=int(os.environ.get("DESECSTACK_API_AUTHACTION_VALIDITY", "0"))
)
-REGISTER_LPS_ON_SIGNUP = bool(
- int(os.environ.get("DESECSTACK_API_REGISTER_LPS_ON_SIGNUP", "1"))
-)
+REGISTER_LPS = bool(int(os.environ.get("DESECSTACK_API_REGISTER_LPS", "1")))
# CAPTCHA
CAPTCHA_VALIDITY_PERIOD = timedelta(hours=24)
diff --git a/api/desecapi/serializers/users.py b/api/desecapi/serializers/users.py
index b20c26526..7135a86e7 100644
--- a/api/desecapi/serializers/users.py
+++ b/api/desecapi/serializers/users.py
@@ -84,14 +84,24 @@ def validate_domain(self, value):
serializer.default_error_messages["name_unavailable"],
code="name_unavailable",
)
+ return value
+
+ def validate(self, attrs):
if (
- not settings.REGISTER_LPS_ON_SIGNUP
- and DomainSerializer.Meta.model(name=value).is_locally_registrable
+ not settings.REGISTER_LPS
+ and attrs.get("captcha") is not None
+ and attrs.get("domain") is not None
+ and DomainSerializer.Meta.model(name=attrs["domain"]).is_locally_registrable
):
raise serializers.ValidationError(
- "Registration during sign-up disabled; please create account without a domain name.",
+ {
+ "domain": [
+ DomainSerializer.default_error_messages["name_unavailable"]
+ ]
+ },
+ code="name_unavailable",
)
- return value
+ return super().validate(attrs)
def create(self, validated_data):
validated_data.pop("domain", None)
diff --git a/api/desecapi/tests/test_domains.py b/api/desecapi/tests/test_domains.py
index cfc101004..05e17b97e 100644
--- a/api/desecapi/tests/test_domains.py
+++ b/api/desecapi/tests/test_domains.py
@@ -1,6 +1,7 @@
from django.conf import settings
from django.core import mail
from django.core.exceptions import ValidationError
+from django.test import override_settings
from rest_framework import status
from desecapi.models import Domain
@@ -714,6 +715,13 @@ def test_create_public_suffixes(self):
self.assertStatus(response, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data["name"][0].code, "name_unavailable")
+ @override_settings(REGISTER_LPS=False)
+ def test_create_local_public_suffixes_lps_disabled(self):
+ domain = "foo." + next(iter(self.AUTO_DELEGATION_DOMAINS))
+ response = self.client.post(self.reverse("v1:domain-list"), {"name": domain})
+ self.assertStatus(response, status.HTTP_400_BAD_REQUEST)
+ self.assertEqual(response.data["name"][0].code, "name_unavailable")
+
def test_create_domain_under_public_suffix_with_private_parent(self):
name = "amazonaws.com"
with self.assertRequests(
diff --git a/api/desecapi/tests/test_user_management.py b/api/desecapi/tests/test_user_management.py
index 24188a2e1..56e66958c 100644
--- a/api/desecapi/tests/test_user_management.py
+++ b/api/desecapi/tests/test_user_management.py
@@ -25,6 +25,7 @@
from django.conf import settings
from django.core import mail
from django.core.management import call_command
+from django.test import override_settings
from django.urls import resolve
from django.utils import timezone
from rest_framework import status
@@ -610,6 +611,19 @@ def test_registration_with_domain(self):
domain=self.random_domain_name(suffix=local_public_suffix)
)
+ @override_settings(REGISTER_LPS=False)
+ def test_registration_with_domain_lps_disabled(self):
+ PublicSuffixMockMixin.setUpMockPatch(self)
+ with self.get_psl_context_manager("."):
+ _, _, domain = self._test_registration_with_domain()
+
+ local_public_suffix = random.sample(list(self.AUTO_DELEGATION_DOMAINS), 1)[0]
+ with self.get_psl_context_manager(local_public_suffix):
+ self._test_registration_with_domain(
+ domain=self.random_domain_name(suffix=local_public_suffix),
+ expect_failure_response=self.assertRegistrationFailureDomainUnavailableResponse,
+ )
+
def test_registration_without_domain_and_password(self):
email, password = self._test_registration(self.random_username(), None)
confirmation_link = self.assertResetPasswordEmail(email)
@@ -693,6 +707,20 @@ def test_registration_wrong_captcha(self):
def test_registration_late_captcha(self):
self._test_registration(password=self.random_password(), late_captcha=True)
+ PublicSuffixMockMixin.setUpMockPatch(self)
+ local_public_suffix = random.sample(list(self.AUTO_DELEGATION_DOMAINS), 1)[0]
+ # Late captcha sign-up allows domain registration (Nextcloud VM workflow)
+ for register_lps in [True, False]:
+ domain = self.random_domain_name(suffix=local_public_suffix)
+ with (
+ override_settings(REGISTER_LPS=register_lps),
+ self.get_psl_context_manager(local_public_suffix),
+ self.assertRequests(
+ self.requests_desec_domain_creation_auto_delegation(domain)
+ ),
+ ):
+ self._test_registration(domain=domain, late_captcha=True)
+
class OtherUserAccountTestCase(UserManagementTestCase):
def setUp(self):
diff --git a/api/desecapi/views/domains.py b/api/desecapi/views/domains.py
index 529ecbb88..6e9b6b611 100644
--- a/api/desecapi/views/domains.py
+++ b/api/desecapi/views/domains.py
@@ -6,6 +6,7 @@
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from rest_framework.response import Response
+from rest_framework.serializers import ValidationError
from rest_framework.settings import api_settings
from rest_framework.views import APIView
@@ -83,6 +84,14 @@ def get_serializer(self, *args, **kwargs):
return super().get_serializer(*args, include_keys=include_keys, **kwargs)
def perform_create(self, serializer):
+ if (
+ not settings.REGISTER_LPS
+ and Domain(name=serializer.validated_data["name"]).is_locally_registrable
+ ):
+ raise ValidationError(
+ {"name": [DomainSerializer.default_error_messages["name_unavailable"]]},
+ code="name_unavailable",
+ )
with PDNSChangeTracker():
domain = serializer.save(owner=self.request.user)
@@ -121,5 +130,5 @@ def get(self, request, *args, **kwargs):
serials = cache.get(key)
if serials is None:
serials = get_serials()
- cache.get_or_set(key, serials, timeout=15)
+ cache.get_or_set(key, serials, timeout=60)
return Response(serials)
diff --git a/api/requirements.txt b/api/requirements.txt
index 155b1d863..e7b2889ba 100644
--- a/api/requirements.txt
+++ b/api/requirements.txt
@@ -1,7 +1,7 @@
captcha~=0.5.0
celery~=5.3.6
-coverage~=7.4.0
-cryptography~=41.0.7
+coverage~=7.4.1
+cryptography~=42.0.1
Django~=5.0.1
django-cors-headers~=4.3.1
djangorestframework~=3.14.0
@@ -9,7 +9,7 @@ django-celery-email~=3.0.0
django-netfields~=1.3.2
django-pgtrigger~=4.11.0
django-prometheus~=2.3.1
-dnspython~=2.4.2
+dnspython~=2.5.0
httpretty~=1.0.5 # 1.1 breaks tests. Does not run in production, so stick to it.
pyotp~=2.9.0
psycopg~=3.1.17
diff --git a/docker-compose.yml b/docker-compose.yml
index 05576cd12..ac83c5b2a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -150,6 +150,8 @@ services:
- DESECSTACK_API_PCH_API
- DESECSTACK_API_PCH_API_TOKEN
- DESECSTACK_API_AUTHACTION_VALIDITY
+ - DESECSTACK_API_REGISTER_LPS
+ - DESECSTACK_API_LIMIT_USER_DOMAIN_COUNT_DEFAULT
- DESECSTACK_DBAPI_PASSWORD_desec
- DESECSTACK_IPV4_REAR_PREFIX16
- DESECSTACK_IPV6_SUBNET
@@ -284,6 +286,7 @@ services:
user: memcache:memcache
networks:
- rearapi_celery
+ command: ["memcached", "-I", "2m"]
logging:
driver: "syslog"
options:
diff --git a/test/e2e2/requirements.txt b/test/e2e2/requirements.txt
index d9ad10d0e..1d9988202 100644
--- a/test/e2e2/requirements.txt
+++ b/test/e2e2/requirements.txt
@@ -2,4 +2,4 @@ pytest
pytest-schema
pytest-xdist
requests
-dnspython~=2.4.2
+dnspython~=2.5.0
diff --git a/www/webapp/package.json b/www/webapp/package.json
index 6100d9797..2fbbcf573 100644
--- a/www/webapp/package.json
+++ b/www/webapp/package.json
@@ -15,7 +15,7 @@
},
"dependencies": {
"@fontsource/roboto": "^5.0.3",
- "@mdi/js": "~7.3.67",
+ "@mdi/js": "~7.4.47",
"axios": "^1.4.0",
"date-fns": "^3.3.1",
"pinia": "^2.0.30",
diff --git a/www/webapp/src/views/HomePage.vue b/www/webapp/src/views/HomePage.vue
index 6bc648949..9f525cb1a 100644
--- a/www/webapp/src/views/HomePage.vue
+++ b/www/webapp/src/views/HomePage.vue
@@ -28,7 +28,7 @@
@change="$router.push({query: {domainType: domainType}})"
>
-
+
-
-
+
+ dynDNS registrations are suspended at this time.
+ We do not process requests for exceptions.
+ Please do not contact support about this. You will have to register a domain elsewhere.
+
+
+