浏览代码

feat(api): allow dynDNS updates regardless of minimum_ttl, closes #393

Peter Thomassen 4 年之前
父节点
当前提交
8499029db3
共有 4 个文件被更改,包括 28 次插入22 次删除
  1. 2 1
      api/desecapi/serializers.py
  2. 9 4
      api/desecapi/tests/test_dyndns12update.py
  3. 1 4
      api/desecapi/views.py
  4. 16 13
      docs/dyndns/update-api.rst

+ 2 - 1
api/desecapi/serializers.py

@@ -426,12 +426,13 @@ class RRsetSerializer(ConditionalExistenceModelSerializer):
             self.domain = self.context['domain']
             self.domain = self.context['domain']
         except KeyError:
         except KeyError:
             raise ValueError('RRsetSerializer() must be given a domain object (to validate uniqueness constraints).')
             raise ValueError('RRsetSerializer() must be given a domain object (to validate uniqueness constraints).')
+        self.minimum_ttl = self.context.get('minimum_ttl', self.domain.minimum_ttl)
 
 
     def get_fields(self):
     def get_fields(self):
         fields = super().get_fields()
         fields = super().get_fields()
         fields['subname'].validators.append(ReadOnlyOnUpdateValidator())
         fields['subname'].validators.append(ReadOnlyOnUpdateValidator())
         fields['type'].validators.append(ReadOnlyOnUpdateValidator())
         fields['type'].validators.append(ReadOnlyOnUpdateValidator())
-        fields['ttl'].validators.append(MinValueValidator(limit_value=self.domain.minimum_ttl))
+        fields['ttl'].validators.append(MinValueValidator(limit_value=self.minimum_ttl))
         return fields
         return fields
 
 
     def get_validators(self):
     def get_validators(self):

+ 9 - 4
api/desecapi/tests/test_dyndns12update.py

@@ -187,12 +187,17 @@ class SingleDomainDynDNS12UpdateTest(DynDNS12UpdateTest):
 class MultipleDomainDynDNS12UpdateTest(DynDNS12UpdateTest):
 class MultipleDomainDynDNS12UpdateTest(DynDNS12UpdateTest):
     NUM_OWNED_DOMAINS = 4
     NUM_OWNED_DOMAINS = 4
 
 
-    def test_honor_minimum_ttl(self):
+    def test_ignore_minimum_ttl(self):
         self.my_domain.minimum_ttl = 61
         self.my_domain.minimum_ttl = 61
         self.my_domain.save()
         self.my_domain.save()
-        response = self.assertDynDNS12NoUpdate(self.my_domain.name)
-        self.assertStatus(response, status.HTTP_403_FORBIDDEN)
-        self.assertEqual(response.data['detail'], 'Domain not eligible for dynamic updates, please contact support.')
+
+        # Test that dynDNS updates work both under a local public suffix (self.my_domain) and for a custom domains
+        for domain in [self.my_domain, self.create_domain(owner=self.owner)]:
+            self.assertGreater(domain.minimum_ttl, 60)
+            self.client.set_credentials_basic_auth(domain.name.lower(), self.token.plain)
+            response = self.assertDynDNS12Update(domain.name)
+            self.assertStatus(response, status.HTTP_200_OK)
+            self.assertEqual(domain.rrset_set.get(subname='', type='A').ttl, 60)
 
 
     def test_identification_by_token(self):
     def test_identification_by_token(self):
         """
         """

+ 1 - 4
api/desecapi/views.py

@@ -364,14 +364,11 @@ class DynDNS12Update(APIView):
         ]
         ]
 
 
         instances = domain.rrset_set.filter(subname='', type__in=['A', 'AAAA']).all()
         instances = domain.rrset_set.filter(subname='', type__in=['A', 'AAAA']).all()
-        context = {'domain': domain}
+        context = {'domain': domain, 'minimum_ttl': 60}
         serializer = serializers.RRsetSerializer(instances, data=data, many=True, partial=True, context=context)
         serializer = serializers.RRsetSerializer(instances, data=data, many=True, partial=True, context=context)
         try:
         try:
             serializer.is_valid(raise_exception=True)
             serializer.is_valid(raise_exception=True)
         except ValidationError as e:
         except ValidationError as e:
-            if any('ttl' in error for error in e.detail):
-                raise PermissionDenied({'detail': 'Domain not eligible for dynamic updates, please contact support.'})
-
             if any(
             if any(
                     any(
                     any(
                         getattr(non_field_error, 'code', '') == 'unique'
                         getattr(non_field_error, 'code', '') == 'unique'

+ 16 - 13
docs/dyndns/update-api.rst

@@ -11,17 +11,23 @@ Please note that when using HTTPS (which we highly recommend), outdated setups
 issues, you may have to update your dynDNS client and/or libraries used by it
 issues, you may have to update your dynDNS client and/or libraries used by it
 (such as OpenSSL).
 (such as OpenSSL).
 
 
+**Note:** Out of mercy for legacy clients (especially old routers), we still
+accept unencrypted requests for this service.  We **urge** you to **use HTTPS
+whenever possible**.
+
 Update Request
 Update Request
 ``````````````
 ``````````````
-An IP updates is performed by sending a GET request to ``update.dedyn.io`` via
-HTTP or HTTPS. The path component can be chosen freely as long as it does not
-end in ``.ico`` or ``.png``.
+An IP updates is performed by sending a ``GET`` request to ``update.dedyn.io``
+via IPv4 or IPv6.  To enforce IPv6, use ``update6.dedyn.io``.  The path
+component can be chosen freely as long as it does not end in ``.ico`` or
+``.png``.  HTTPS is recommended over HTTP.
 
 
-You can connect via IPv4 or IPv6. To enforce IPv6, use ``update6.dedyn.io``.
+When the request is authenticated successfully, we use the connection IP
+address and query parameters to update your domain's DNS ``A`` (IPv4) and
+``AAAA`` (IPv6) records.  The new records will have a TTL value of 60 seconds
+(that is, outdated values should disappear from DNS resolvers within that
+time).
 
 
-Please be aware that while we still accept unencrypted requests, we **urge**
-you to use HTTPS. For that reason, we also send an HSTS header on HTTPS
-connections.
 
 
 .. _update-api-authentication:
 .. _update-api-authentication:
 
 
@@ -78,10 +84,7 @@ determine the hostname, we try the following steps until there is a match:
   associated with your user account (if not ambiguous).
   associated with your user account (if not ambiguous).
 
 
 If we cannot determine a hostname to update, the API will return a ``404 Not
 If we cannot determine a hostname to update, the API will return a ``404 Not
-Found`` status code. If the selected hostname is not eligible for dynamic
-updates, we will return ``403 Forbidden``. This usually happens if you try
-updating a hostname that is not under the ``dedyn.io`` domain. If you are
-affected by this and would like to use another domain, please contact support.
+Found`` status code.
 
 
 .. _determine-ip-addresses:
 .. _determine-ip-addresses:
 
 
@@ -118,8 +121,8 @@ Examples
 ````````
 ````````
 The examples below use ``<your domain>.dedyn.io`` as the domain which is to be updated and
 The examples below use ``<your domain>.dedyn.io`` as the domain which is to be updated and
 ``<your authorization token>`` as an API token affiliated with the respective account.
 ``<your authorization token>`` as an API token affiliated with the respective account.
-(See :ref:`manage-tokens` for details.) ``<1.2.3.4>`` is used as an example for an IPv4 Address,
-``<fd08::1234>`` as a standin for an IPv6 address. Replace those (including the ``<`` and ``>``)
+(See :ref:`manage-tokens` for details.) ``1.2.3.4`` is used as an example for an IPv4 Address,
+``fd08::1234`` as a stand-in for an IPv6 address. Replace those (including the ``<`` and ``>``)
 with your respective values.
 with your respective values.