]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Encode subnets not a a single path component, but as id/prefixlen as auth does
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 15 Jan 2026 12:12:17 +0000 (13:12 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 15 Jan 2026 12:12:17 +0000 (13:12 +0100)
Signed-off-by: Otto Moerbeek <otto.moerbeek@open-xchange.com>
pdns/recursordist/docs/http-api/endpoint-ottraceconditions.rst
pdns/recursordist/rec-rust-lib/rust/src/web.rs
regression-tests.api/test_RecursorOTConditions.py

index 88dc84253613f9efe8ad39e6b09db5baa4c32f01..3a0f95d03e4c102636757b6475bc273eb8c3962b 100644 (file)
@@ -17,16 +17,16 @@ OpenTelemetryTraceConditions endpoint
 
   :query server_id: The name of the server
 
-.. http:get:: /api/v1/servers/:server_id/ottraceconditions/:subnet
+.. http:get:: /api/v1/servers/:server_id/ottraceconditions/:ip/:prefixlen
 
   Returns trace condition information.
 
   :query server_id: The name of the server
-  :query subnet: The subnet of the :json:object:`OpenTelemetryTraceCondition`. URL encode subnet, for example ``192.0.2.1/32`` becomes ``192.0.2.1%2F32``.
+  :query ip/prefixlen: The subnet of the :json:object:`OpenTelemetryTraceCondition`.
 
-.. http:delete:: /api/v1/servers/:server_id/ottraceconditions/:subnet
+.. http:delete:: /api/v1/servers/:server_id/ottraceconditions/:ip/:prefixlen
 
   Deletes this zone, all attached metadata and rrsets.
 
   :query server_id: The name of the server
-  :query subnet: The subnet of the :json:object:`OpenTelemetryTraceCondition`. URL encode subnet, for example ``192.0.2.1/32`` becomes ``192.0.2.1%2F32``.
+  :query ip/prefixlen: The subnet of the :json:object:`OpenTelemetryTraceCondition`.
index e06ffbdb2d95b7bdac1e0f121751954d49f08d56..bb6c91b9e070b1f9f8ab0bc47903ff56481ce183 100644 (file)
@@ -402,26 +402,18 @@ fn matcher(
         (&Method::GET, ["api", "v1", "servers", "localhost", "ottraceconditions"]) => {
             *apifunc = Some(rustweb::apiServerOTConditionsGET);
         }
-        (&Method::GET, ["api", "v1", "servers", "localhost", "ottraceconditions", acl]) => {
-            let decoded = form_urlencoded::parse(acl.as_bytes());
-            // decoded should contain a single key without value
-            if let Some(kv) = decoded.last() {
-                request.parameters.push(rustweb::KeyValue {
-                    key: String::from("acl"),
-                    value: kv.0.to_string(),
-                });
-            }
+        (&Method::GET, ["api", "v1", "servers", "localhost", "ottraceconditions", ip, pflen]) => {
+            request.parameters.push(rustweb::KeyValue {
+                key: String::from("acl"),
+                value: String::from(*ip) + "/" + *pflen,
+            });
             *apifunc = Some(rustweb::apiServerOTConditionDetailGET)
         }
-        (&Method::DELETE, ["api", "v1", "servers", "localhost", "ottraceconditions", acl]) => {
-            let decoded = form_urlencoded::parse(acl.as_bytes());
-            // decoded should contain a single key without value
-            if let Some(kv) = decoded.last() {
-                request.parameters.push(rustweb::KeyValue {
-                    key: String::from("acl"),
-                    value: kv.0.to_string(),
-                });
-            }
+        (&Method::DELETE, ["api", "v1", "servers", "localhost", "ottraceconditions", ip, pflen]) => {
+            request.parameters.push(rustweb::KeyValue {
+                key: String::from("acl"),
+                value: String::from(*ip) + "/" + *pflen,
+            });
             *apifunc = Some(rustweb::apiServerOTConditionDetailDELETE)
         }
         (&Method::POST, ["api", "v1", "servers", "localhost", "ottraceconditions"]) => {
index c282a3b2cad0937ded498fd9d60813aa203a3472..091b3ef66845cfb90b3c4be6ae0348e3f3664303 100644 (file)
@@ -28,21 +28,21 @@ class RecursorOT(ApiTestCase):
 
         # nonexistent condition
         r = self.session.get(
-            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4%2F32"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 422)
         self.assert_in_json_error('Could not find otcondition', r.json())
 
         # malformed netmask
         r = self.session.get(
-            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3%2F32"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3/32"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 422)
         self.assert_in_json_error('Could not parse netmask', r.json())
 
         # deleting non-existent netmask
         r = self.session.delete(
-            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4%2F32"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 422)
         self.assert_in_json_error('Could not find otcondition', r.json())
@@ -108,16 +108,16 @@ class RecursorOT(ApiTestCase):
         self.assertEqual(r.status_code, 200)
         self.assertEqual(len(r.json()), 2)
 
-        # querying by more specific key
+        # querying by more specific key than /24
         r = self.session.get(
-            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4%2F31"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/31"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 422)
         self.assert_in_json_error('Could not find otcondition', r.json())
 
         # deleting specific netmask
         r = self.session.delete(
-            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4%2F32"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/1.2.3.4/32"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 204)
 
@@ -160,7 +160,7 @@ class RecursorOT(ApiTestCase):
 
         # and GET the newly created one in a separate call
         r = self.session.get(
-            self.url("/api/v1/servers/localhost/ottraceconditions/::1%2F0"),
+            self.url("/api/v1/servers/localhost/ottraceconditions/::/0"),
             headers={'content-type': 'application/json'})
         self.assertEqual(r.status_code, 200)
         data = r.json()