]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
API (Auth): fix hosting of root zone 2280/head
authorChristian Hofstaedtler <christian@hofstaedtler.name>
Tue, 24 Feb 2015 22:46:27 +0000 (23:46 +0100)
committerChristian Hofstaedtler <christian@hofstaedtler.name>
Tue, 24 Feb 2015 22:48:18 +0000 (23:48 +0100)
As discovered by @jpmens in #2216, the API could not create the root
zone, and listing zones would also fail when the root zone was present.

This corrects those bugs, plus another that prevented reading the root
zone, and adds a small API test set for the root zone.

Fixes #2216.

pdns/ws-api.cc
pdns/ws-auth.cc
regression-tests.api/test_Zones.py

index 41389f3664d0571b242ddd8ce19de9f78df2eb18..a42f1a6fb95fbc048b77c47b97b402244fa1e141 100644 (file)
@@ -262,7 +262,7 @@ string apiZoneIdToName(const string& id) {
   zonename = ss.str();
 
   // strip trailing dot
-  if (zonename.substr(zonename.size()-1) == ".") {
+  if (zonename.size() > 0 && zonename.substr(zonename.size()-1) == ".") {
     zonename.resize(zonename.size()-1);
   }
   return zonename;
@@ -285,14 +285,14 @@ string apiZoneNameToId(const string& name) {
   string id = ss.str();
 
   // add trailing dot
-  if (id.substr(id.size()-1) != ".") {
+  if (id.size() == 0 || id.substr(id.size()-1) != ".") {
     id += ".";
   }
 
   // special handling for the root zone, as a dot on it's own doesn't work
   // everywhere.
   if (id == ".") {
-    id = (boost::format("=%02x") % (int)('.')).str();
+    id = (boost::format("=%02X") % (int)('.')).str();
   }
   return id;
 }
index 5214619dc2bc589e468965d2fb005e6639c30b60..7b35a91b6460bd0ac6cdeedd9e80f90c249c8f01 100644 (file)
@@ -601,18 +601,15 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
     Document document;
     req->json(document);
     string zonename = stringFromJson(document, "name");
-    string dotsuffix = "." + zonename;
-    string zonestring = stringFromJson(document, "zone", "");
-
-    // TODO: better validation of zonename
-    if(zonename.empty())
-      throw ApiException("Zone name empty");
 
-    // strip any trailing dots
-    while (zonename.substr(zonename.size()-1) == ".") {
+    // strip trailing dot (from spec PoV this is wrong, but be nice to clients)
+    if (zonename.size() > 0 && zonename.substr(zonename.size()-1) == ".") {
       zonename.resize(zonename.size()-1);
     }
 
+    string dotsuffix = "." + zonename;
+    string zonestring = stringFromJson(document, "zone", "");
+
     bool exists = B.getDomainInfo(zonename, di);
     if(exists)
       throw ApiException("Domain '"+zonename+"' already exists");
index a035c875d1972313a8ace2f608c8e40970f788d1..a894589b4bdda5adb3f228297bc8ed4738c68ea2 100644 (file)
@@ -22,9 +22,7 @@ class Zones(ApiTestCase):
             self.assertIn(field, example_com)
 
 
-@unittest.skipIf(not is_auth(), "Not applicable")
-class AuthZones(ApiTestCase):
-
+class AuthZonesHelperMixin(object):
     def create_zone(self, name=None, **kwargs):
         if name is None:
             name = unique_zone_name()
@@ -47,6 +45,10 @@ class AuthZones(ApiTestCase):
         self.assertEquals(r.status_code, 201)
         return payload, r.json()
 
+
+@unittest.skipIf(not is_auth(), "Not applicable")
+class AuthZones(ApiTestCase, AuthZonesHelperMixin):
+
     def test_create_zone(self):
         payload, data = self.create_zone(serial=22)
         for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'):
@@ -1004,6 +1006,78 @@ fred   IN  A      192.168.0.4
         self.assertEquals(len(r.json()), 1)  # FIXME test disarmed for now (should be 4)
 
 
+@unittest.skipIf(not is_auth(), "Not applicable")
+class AuthRootZone(ApiTestCase, AuthZonesHelperMixin):
+
+    def setUp(self):
+        super(AuthRootZone, self).setUp()
+        # zone name is not unique, so delete the zone before each individual test.
+        self.session.delete(self.url("/servers/localhost/zones/=2E"))
+
+    def test_create_zone(self):
+        payload, data = self.create_zone(name='', serial=22)
+        for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'):
+            self.assertIn(k, data)
+            if k in payload:
+                self.assertEquals(data[k], payload[k])
+        self.assertEquals(data['comments'], [])
+        # validate generated SOA
+        self.assertEquals(
+            [r['content'] for r in data['records'] if r['type'] == 'SOA'][0],
+            "a.misconfigured.powerdns.server hostmaster." + payload['name'] + " " + str(payload['serial']) +
+            " 10800 3600 604800 3600"
+        )
+        # Regression test: verify zone list works
+        zonelist = self.session.get(self.url("/servers/localhost/zones")).json()
+        print "zonelist:", zonelist
+        self.assertIn(payload['name'], [zone['name'] for zone in zonelist])
+        # Also test that fetching the zone works.
+        print "id:", data['id']
+        self.assertEquals(data['id'], '=2E')
+        data = self.session.get(self.url("/servers/localhost/zones/" + data['id'])).json()
+        print "zone (fetched):", data
+        for k in ('name', 'kind'):
+            self.assertIn(k, data)
+            self.assertEquals(data[k], payload[k])
+        self.assertEqual(data['records'][0]['name'], '')
+
+    def test_update_zone(self):
+        payload, zone = self.create_zone(name='')
+        name = ''
+        zone_id = '=2E'
+        # update, set as Master and enable SOA-EDIT-API
+        payload = {
+            'kind': 'Master',
+            'masters': ['192.0.2.1', '192.0.2.2'],
+            'soa_edit_api': 'EPOCH',
+            'soa_edit': 'EPOCH'
+        }
+        r = self.session.put(
+            self.url("/servers/localhost/zones/" + zone_id),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assert_success_json(r)
+        data = r.json()
+        for k in payload.keys():
+            self.assertIn(k, data)
+            self.assertEquals(data[k], payload[k])
+        # update, back to Native and empty(off)
+        payload = {
+            'kind': 'Native',
+            'soa_edit_api': '',
+            'soa_edit': ''
+        }
+        r = self.session.put(
+            self.url("/servers/localhost/zones/" + zone_id),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assert_success_json(r)
+        data = r.json()
+        for k in payload.keys():
+            self.assertIn(k, data)
+            self.assertEquals(data[k], payload[k])
+
+
 @unittest.skipIf(not is_recursor(), "Not applicable")
 class RecursorZones(ApiTestCase):