]> granicus.if.org Git - pdns/commitdiff
API (Auth): fix hosting of root zone
authorChristian Hofstaedtler <christian@hofstaedtler.name>
Tue, 24 Feb 2015 22:46:27 +0000 (23:46 +0100)
committermind04 <mind04@monshouwer.org>
Tue, 21 Apr 2015 13:18:39 +0000 (15:18 +0200)
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.

(cherry picked from commit 406497f50ca776c26db3df4a56bf560095c4db32)

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

index 44af01b383584b53ddbf2434359d91e5af9b47a3..0efc7577516c7135b22b4119816b6bb5b8ad86d4 100644 (file)
@@ -259,7 +259,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;
@@ -282,14 +282,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 8c785bca7accb1734abf67f6fd4225c04f0d973f..d17399825da6d085c0c57c435dc02f672be6b808 100644 (file)
@@ -628,18 +628,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 e92c215d552861970baa683c0350f625bcabadd5..1c6d0b21567e18c6163aca636d1ea1e839478fea 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'):
@@ -1003,6 +1005,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):