]>
Commit | Line | Data |
---|---|---|
541bb91b | 1 | from __future__ import print_function |
e2dba705 | 2 | import json |
d29d5db7 | 3 | import time |
e2dba705 | 4 | import unittest |
ccfabd0d | 5 | from copy import deepcopy |
6754ef71 | 6 | from pprint import pprint |
7cbc5255 | 7 | from test_helper import ApiTestCase, unique_zone_name, is_auth, is_recursor, get_db_records, pdnsutil_rectify |
6754ef71 CH |
8 | |
9 | ||
10 | def get_rrset(data, qname, qtype): | |
11 | for rrset in data['rrsets']: | |
12 | if rrset['name'] == qname and rrset['type'] == qtype: | |
13 | return rrset | |
14 | return None | |
15 | ||
16 | ||
17 | def get_first_rec(data, qname, qtype): | |
18 | rrset = get_rrset(data, qname, qtype) | |
19 | if rrset: | |
20 | return rrset['records'][0] | |
21 | return None | |
22 | ||
23 | ||
24 | def eq_zone_rrsets(rrsets, expected): | |
25 | data_got = {} | |
26 | data_expected = {} | |
541bb91b | 27 | for type_, expected_records in expected.items(): |
6754ef71 CH |
28 | type_ = str(type_) |
29 | data_got[type_] = set() | |
30 | data_expected[type_] = set() | |
31 | uses_name = any(['name' in expected_record for expected_record in expected_records]) | |
32 | # minify + convert received data | |
33 | for rrset in [rrset for rrset in rrsets if rrset['type'] == type_]: | |
541bb91b | 34 | print(rrset) |
6754ef71 CH |
35 | for r in rrset['records']: |
36 | data_got[type_].add((rrset['name'] if uses_name else '@', rrset['type'], r['content'])) | |
37 | # minify expected data | |
38 | for r in expected_records: | |
39 | data_expected[type_].add((r['name'] if uses_name else '@', type_, r['content'])) | |
40 | ||
541bb91b | 41 | print("eq_zone_rrsets: got:") |
6754ef71 | 42 | pprint(data_got) |
541bb91b | 43 | print("eq_zone_rrsets: expected:") |
6754ef71 CH |
44 | pprint(data_expected) |
45 | ||
46 | assert data_got == data_expected, "%r != %r" % (data_got, data_expected) | |
1a152698 CH |
47 | |
48 | ||
02945d9a | 49 | class Zones(ApiTestCase): |
1a152698 | 50 | |
c1374bdb | 51 | def test_list_zones(self): |
46d06a12 | 52 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
c1374bdb | 53 | self.assert_success_json(r) |
45de6290 | 54 | domains = r.json() |
02945d9a | 55 | example_com = [domain for domain in domains if domain['name'] in ('example.com', 'example.com.')] |
1a152698 CH |
56 | self.assertEquals(len(example_com), 1) |
57 | example_com = example_com[0] | |
a21e8566 | 58 | print(example_com) |
02945d9a | 59 | required_fields = ['id', 'url', 'name', 'kind'] |
c1374bdb | 60 | if is_auth(): |
c04b5870 | 61 | required_fields = required_fields + ['masters', 'last_check', 'notified_serial', 'serial', 'account'] |
a21e8566 | 62 | self.assertNotEquals(example_com['serial'], 0) |
c1374bdb | 63 | elif is_recursor(): |
02945d9a CH |
64 | required_fields = required_fields + ['recursion_desired', 'servers'] |
65 | for field in required_fields: | |
66 | self.assertIn(field, example_com) | |
67 | ||
68 | ||
406497f5 | 69 | class AuthZonesHelperMixin(object): |
284fdfe9 | 70 | def create_zone(self, name=None, **kwargs): |
bee2acae CH |
71 | if name is None: |
72 | name = unique_zone_name() | |
e2dba705 | 73 | payload = { |
bee2acae | 74 | 'name': name, |
e2dba705 | 75 | 'kind': 'Native', |
1d6b70f9 | 76 | 'nameservers': ['ns1.example.com.', 'ns2.example.com.'] |
e2dba705 | 77 | } |
284fdfe9 | 78 | for k, v in kwargs.items(): |
4bdff352 CH |
79 | if v is None: |
80 | del payload[k] | |
81 | else: | |
82 | payload[k] = v | |
541bb91b | 83 | print("sending", payload) |
e2dba705 | 84 | r = self.session.post( |
46d06a12 | 85 | self.url("/api/v1/servers/localhost/zones"), |
e2dba705 CH |
86 | data=json.dumps(payload), |
87 | headers={'content-type': 'application/json'}) | |
c1374bdb | 88 | self.assert_success_json(r) |
64a36f0d | 89 | self.assertEquals(r.status_code, 201) |
1d6b70f9 | 90 | reply = r.json() |
541bb91b | 91 | print("reply", reply) |
6754ef71 | 92 | return name, payload, reply |
bee2acae | 93 | |
406497f5 CH |
94 | |
95 | @unittest.skipIf(not is_auth(), "Not applicable") | |
96 | class AuthZones(ApiTestCase, AuthZonesHelperMixin): | |
97 | ||
c1374bdb | 98 | def test_create_zone(self): |
b0af9105 | 99 | # soa_edit_api has a default, override with empty for this test |
6754ef71 | 100 | name, payload, data = self.create_zone(serial=22, soa_edit_api='') |
8ffb7a9b | 101 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'): |
d29d5db7 CH |
102 | self.assertIn(k, data) |
103 | if k in payload: | |
104 | self.assertEquals(data[k], payload[k]) | |
f63168e6 | 105 | # validate generated SOA |
6754ef71 | 106 | expected_soa = "a.misconfigured.powerdns.server. hostmaster." + name + " " + \ |
1d6b70f9 | 107 | str(payload['serial']) + " 10800 3600 604800 3600" |
f63168e6 | 108 | self.assertEquals( |
6754ef71 | 109 | get_first_rec(data, name, 'SOA')['content'], |
1d6b70f9 | 110 | expected_soa |
f63168e6 | 111 | ) |
1d6b70f9 | 112 | # Because we had confusion about dots, check that the DB is without dots. |
6754ef71 | 113 | dbrecs = get_db_records(name, 'SOA') |
1d6b70f9 | 114 | self.assertEqual(dbrecs[0]['content'], expected_soa.replace('. ', ' ')) |
d29d5db7 | 115 | |
c1374bdb | 116 | def test_create_zone_with_soa_edit_api(self): |
f63168e6 | 117 | # soa_edit_api wins over serial |
6754ef71 | 118 | name, payload, data = self.create_zone(soa_edit_api='EPOCH', serial=10) |
f63168e6 | 119 | for k in ('soa_edit_api', ): |
e2dba705 CH |
120 | self.assertIn(k, data) |
121 | if k in payload: | |
122 | self.assertEquals(data[k], payload[k]) | |
f63168e6 | 123 | # generated EPOCH serial surely is > fixed serial we passed in |
541bb91b | 124 | print(data) |
f63168e6 | 125 | self.assertGreater(data['serial'], payload['serial']) |
6754ef71 | 126 | soa_serial = int(get_first_rec(data, name, 'SOA')['content'].split(' ')[2]) |
f63168e6 CH |
127 | self.assertGreater(soa_serial, payload['serial']) |
128 | self.assertEquals(soa_serial, data['serial']) | |
6bb25159 | 129 | |
79532aa7 CH |
130 | def test_create_zone_with_account(self): |
131 | # soa_edit_api wins over serial | |
6754ef71 | 132 | name, payload, data = self.create_zone(account='anaccount', serial=10) |
541bb91b | 133 | print(data) |
79532aa7 CH |
134 | for k in ('account', ): |
135 | self.assertIn(k, data) | |
136 | if k in payload: | |
137 | self.assertEquals(data[k], payload[k]) | |
138 | ||
9440a9f0 CH |
139 | def test_create_zone_default_soa_edit_api(self): |
140 | name, payload, data = self.create_zone() | |
541bb91b | 141 | print(data) |
9440a9f0 CH |
142 | self.assertEquals(data['soa_edit_api'], 'DEFAULT') |
143 | ||
331d3062 CH |
144 | def test_create_zone_exists(self): |
145 | name, payload, data = self.create_zone() | |
146 | print(data) | |
147 | payload = { | |
148 | 'name': name, | |
149 | 'kind': 'Native' | |
150 | } | |
151 | print(payload) | |
152 | r = self.session.post( | |
153 | self.url("/api/v1/servers/localhost/zones"), | |
154 | data=json.dumps(payload), | |
155 | headers={'content-type': 'application/json'}) | |
156 | self.assertEquals(r.status_code, 409) # Conflict - already exists | |
157 | ||
01f7df3f CH |
158 | def test_create_zone_with_soa_edit(self): |
159 | name, payload, data = self.create_zone(soa_edit='INCEPTION-INCREMENT', soa_edit_api='SOA-EDIT-INCREASE') | |
541bb91b | 160 | print(data) |
01f7df3f CH |
161 | self.assertEquals(data['soa_edit'], 'INCEPTION-INCREMENT') |
162 | self.assertEquals(data['soa_edit_api'], 'SOA-EDIT-INCREASE') | |
163 | soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2] | |
164 | # These particular settings lead to the first serial set to YYYYMMDD01. | |
165 | self.assertEquals(soa_serial[-2:], '01') | |
f613d242 CH |
166 | rrset = { |
167 | 'changetype': 'replace', | |
168 | 'name': name, | |
169 | 'type': 'A', | |
170 | 'ttl': 3600, | |
171 | 'records': [ | |
172 | { | |
173 | "content": "127.0.0.1", | |
174 | "disabled": False | |
175 | } | |
176 | ] | |
177 | } | |
178 | payload = {'rrsets': [rrset]} | |
179 | self.session.patch( | |
180 | self.url("/api/v1/servers/localhost/zones/" + data['id']), | |
181 | data=json.dumps(payload), | |
182 | headers={'content-type': 'application/json'}) | |
183 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])) | |
184 | data = r.json() | |
185 | soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2] | |
186 | self.assertEquals(soa_serial[-2:], '02') | |
01f7df3f | 187 | |
c1374bdb | 188 | def test_create_zone_with_records(self): |
f63168e6 | 189 | name = unique_zone_name() |
6754ef71 CH |
190 | rrset = { |
191 | "name": name, | |
192 | "type": "A", | |
193 | "ttl": 3600, | |
194 | "records": [{ | |
f63168e6 | 195 | "content": "4.3.2.1", |
6754ef71 CH |
196 | "disabled": False, |
197 | }], | |
198 | } | |
199 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
f63168e6 | 200 | # check our record has appeared |
6754ef71 | 201 | self.assertEquals(get_rrset(data, name, 'A')['records'], rrset['records']) |
f63168e6 | 202 | |
d0953126 AT |
203 | def test_create_zone_with_wildcard_records(self): |
204 | name = unique_zone_name() | |
6754ef71 CH |
205 | rrset = { |
206 | "name": "*."+name, | |
207 | "type": "A", | |
208 | "ttl": 3600, | |
209 | "records": [{ | |
d0953126 | 210 | "content": "4.3.2.1", |
6754ef71 CH |
211 | "disabled": False, |
212 | }], | |
213 | } | |
214 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
d0953126 | 215 | # check our record has appeared |
6754ef71 | 216 | self.assertEquals(get_rrset(data, rrset['name'], 'A')['records'], rrset['records']) |
d0953126 | 217 | |
c1374bdb | 218 | def test_create_zone_with_comments(self): |
f63168e6 | 219 | name = unique_zone_name() |
f2d6dcc0 RG |
220 | rrsets = [ |
221 | { | |
222 | "name": name, | |
223 | "type": "soa", # test uppercasing of type, too. | |
224 | "comments": [{ | |
225 | "account": "test1", | |
226 | "content": "blah blah", | |
227 | "modified_at": 11112, | |
228 | }], | |
229 | }, | |
230 | { | |
231 | "name": name, | |
232 | "type": "AAAA", | |
233 | "ttl": 3600, | |
234 | "records": [{ | |
235 | "content": "2001:DB8::1", | |
236 | "disabled": False, | |
237 | }], | |
238 | "comments": [{ | |
239 | "account": "test AAAA", | |
240 | "content": "blah blah AAAA", | |
241 | "modified_at": 11112, | |
242 | }], | |
243 | }, | |
244 | { | |
245 | "name": name, | |
246 | "type": "TXT", | |
247 | "ttl": 3600, | |
248 | "records": [{ | |
249 | "content": "\"test TXT\"", | |
250 | "disabled": False, | |
251 | }], | |
252 | }, | |
253 | { | |
254 | "name": name, | |
255 | "type": "A", | |
256 | "ttl": 3600, | |
257 | "records": [{ | |
258 | "content": "192.0.2.1", | |
259 | "disabled": False, | |
260 | }], | |
261 | }, | |
262 | ] | |
263 | name, payload, data = self.create_zone(name=name, rrsets=rrsets) | |
264 | # NS records have been created | |
265 | self.assertEquals(len(data['rrsets']), len(rrsets) + 1) | |
f63168e6 | 266 | # check our comment has appeared |
f2d6dcc0 RG |
267 | self.assertEquals(get_rrset(data, name, 'SOA')['comments'], rrsets[0]['comments']) |
268 | self.assertEquals(get_rrset(data, name, 'A')['comments'], []) | |
269 | self.assertEquals(get_rrset(data, name, 'TXT')['comments'], []) | |
270 | self.assertEquals(get_rrset(data, name, 'AAAA')['comments'], rrsets[1]['comments']) | |
f63168e6 | 271 | |
1d6b70f9 CH |
272 | def test_create_zone_uncanonical_nameservers(self): |
273 | name = unique_zone_name() | |
274 | payload = { | |
275 | 'name': name, | |
276 | 'kind': 'Native', | |
277 | 'nameservers': ['uncanon.example.com'] | |
278 | } | |
541bb91b | 279 | print(payload) |
1d6b70f9 CH |
280 | r = self.session.post( |
281 | self.url("/api/v1/servers/localhost/zones"), | |
282 | data=json.dumps(payload), | |
283 | headers={'content-type': 'application/json'}) | |
284 | self.assertEquals(r.status_code, 422) | |
285 | self.assertIn('Nameserver is not canonical', r.json()['error']) | |
286 | ||
287 | def test_create_auth_zone_no_name(self): | |
288 | name = unique_zone_name() | |
289 | payload = { | |
290 | 'name': '', | |
291 | 'kind': 'Native', | |
292 | } | |
541bb91b | 293 | print(payload) |
1d6b70f9 CH |
294 | r = self.session.post( |
295 | self.url("/api/v1/servers/localhost/zones"), | |
296 | data=json.dumps(payload), | |
297 | headers={'content-type': 'application/json'}) | |
298 | self.assertEquals(r.status_code, 422) | |
299 | self.assertIn('is not canonical', r.json()['error']) | |
300 | ||
c1374bdb | 301 | def test_create_zone_with_custom_soa(self): |
f63168e6 | 302 | name = unique_zone_name() |
6754ef71 CH |
303 | content = u"ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600" |
304 | rrset = { | |
305 | "name": name, | |
306 | "type": "soa", # test uppercasing of type, too. | |
307 | "ttl": 3600, | |
308 | "records": [{ | |
309 | "content": content, | |
310 | "disabled": False, | |
311 | }], | |
312 | } | |
313 | name, payload, data = self.create_zone(name=name, rrsets=[rrset], soa_edit_api='') | |
314 | self.assertEquals(get_rrset(data, name, 'SOA')['records'], rrset['records']) | |
315 | dbrecs = get_db_records(name, 'SOA') | |
316 | self.assertEqual(dbrecs[0]['content'], content.replace('. ', ' ')) | |
1d6b70f9 CH |
317 | |
318 | def test_create_zone_double_dot(self): | |
319 | name = 'test..' + unique_zone_name() | |
320 | payload = { | |
321 | 'name': name, | |
322 | 'kind': 'Native', | |
323 | 'nameservers': ['ns1.example.com.'] | |
324 | } | |
541bb91b | 325 | print(payload) |
1d6b70f9 CH |
326 | r = self.session.post( |
327 | self.url("/api/v1/servers/localhost/zones"), | |
328 | data=json.dumps(payload), | |
329 | headers={'content-type': 'application/json'}) | |
330 | self.assertEquals(r.status_code, 422) | |
331 | self.assertIn('Unable to parse DNS Name', r.json()['error']) | |
05776d2f | 332 | |
1d6b70f9 CH |
333 | def test_create_zone_restricted_chars(self): |
334 | name = 'test:' + unique_zone_name() # : isn't good as a name. | |
335 | payload = { | |
336 | 'name': name, | |
337 | 'kind': 'Native', | |
338 | 'nameservers': ['ns1.example.com'] | |
339 | } | |
541bb91b | 340 | print(payload) |
1d6b70f9 CH |
341 | r = self.session.post( |
342 | self.url("/api/v1/servers/localhost/zones"), | |
343 | data=json.dumps(payload), | |
344 | headers={'content-type': 'application/json'}) | |
345 | self.assertEquals(r.status_code, 422) | |
346 | self.assertIn('contains unsupported characters', r.json()['error']) | |
4ebf78b1 | 347 | |
33e6c3e9 CH |
348 | def test_create_zone_mixed_nameservers_ns_rrset_zonelevel(self): |
349 | name = unique_zone_name() | |
350 | rrset = { | |
351 | "name": name, | |
352 | "type": "NS", | |
353 | "ttl": 3600, | |
354 | "records": [{ | |
355 | "content": "ns2.example.com.", | |
356 | "disabled": False, | |
357 | }], | |
358 | } | |
359 | payload = { | |
360 | 'name': name, | |
361 | 'kind': 'Native', | |
362 | 'nameservers': ['ns1.example.com.'], | |
363 | 'rrsets': [rrset], | |
364 | } | |
541bb91b | 365 | print(payload) |
33e6c3e9 CH |
366 | r = self.session.post( |
367 | self.url("/api/v1/servers/localhost/zones"), | |
368 | data=json.dumps(payload), | |
369 | headers={'content-type': 'application/json'}) | |
370 | self.assertEquals(r.status_code, 422) | |
371 | self.assertIn('Nameservers list MUST NOT be mixed with zone-level NS in rrsets', r.json()['error']) | |
372 | ||
373 | def test_create_zone_mixed_nameservers_ns_rrset_below_zonelevel(self): | |
374 | name = unique_zone_name() | |
375 | rrset = { | |
376 | "name": 'subzone.'+name, | |
377 | "type": "NS", | |
378 | "ttl": 3600, | |
379 | "records": [{ | |
380 | "content": "ns2.example.com.", | |
381 | "disabled": False, | |
382 | }], | |
383 | } | |
384 | payload = { | |
385 | 'name': name, | |
386 | 'kind': 'Native', | |
387 | 'nameservers': ['ns1.example.com.'], | |
388 | 'rrsets': [rrset], | |
389 | } | |
541bb91b | 390 | print(payload) |
33e6c3e9 CH |
391 | r = self.session.post( |
392 | self.url("/api/v1/servers/localhost/zones"), | |
393 | data=json.dumps(payload), | |
394 | headers={'content-type': 'application/json'}) | |
395 | self.assert_success_json(r) | |
396 | ||
c1374bdb | 397 | def test_create_zone_with_symbols(self): |
6754ef71 | 398 | name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name()) |
bee2acae | 399 | name = payload['name'] |
1d6b70f9 | 400 | expected_id = name.replace('/', '=2F') |
00a9b229 CH |
401 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'): |
402 | self.assertIn(k, data) | |
403 | if k in payload: | |
404 | self.assertEquals(data[k], payload[k]) | |
bee2acae | 405 | self.assertEquals(data['id'], expected_id) |
1d6b70f9 CH |
406 | dbrecs = get_db_records(name, 'SOA') |
407 | self.assertEqual(dbrecs[0]['name'], name.rstrip('.')) | |
00a9b229 | 408 | |
c1374bdb | 409 | def test_create_zone_with_nameservers_non_string(self): |
e90b4e38 CH |
410 | # ensure we don't crash |
411 | name = unique_zone_name() | |
412 | payload = { | |
413 | 'name': name, | |
414 | 'kind': 'Native', | |
415 | 'nameservers': [{'a': 'ns1.example.com'}] # invalid | |
416 | } | |
541bb91b | 417 | print(payload) |
e90b4e38 | 418 | r = self.session.post( |
46d06a12 | 419 | self.url("/api/v1/servers/localhost/zones"), |
e90b4e38 CH |
420 | data=json.dumps(payload), |
421 | headers={'content-type': 'application/json'}) | |
422 | self.assertEquals(r.status_code, 422) | |
423 | ||
986e4858 PL |
424 | def test_create_zone_with_dnssec(self): |
425 | """ | |
426 | Create a zone with "dnssec" set and see if a key was made. | |
427 | """ | |
428 | name = unique_zone_name() | |
429 | name, payload, data = self.create_zone(dnssec=True) | |
430 | ||
431 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
432 | ||
433 | for k in ('dnssec', ): | |
434 | self.assertIn(k, data) | |
435 | if k in payload: | |
436 | self.assertEquals(data[k], payload[k]) | |
437 | ||
438 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/cryptokeys')) | |
439 | ||
440 | keys = r.json() | |
441 | ||
541bb91b | 442 | print(keys) |
986e4858 PL |
443 | |
444 | self.assertEquals(r.status_code, 200) | |
445 | self.assertEquals(len(keys), 1) | |
446 | self.assertEquals(keys[0]['type'], 'Cryptokey') | |
447 | self.assertEquals(keys[0]['active'], True) | |
448 | self.assertEquals(keys[0]['keytype'], 'csk') | |
449 | ||
cbe8b186 PL |
450 | def test_create_zone_with_dnssec_disable_dnssec(self): |
451 | """ | |
452 | Create a zone with "dnssec", then set "dnssec" to false and see if the | |
453 | keys are gone | |
454 | """ | |
455 | name = unique_zone_name() | |
456 | name, payload, data = self.create_zone(dnssec=True) | |
457 | ||
458 | self.session.put(self.url("/api/v1/servers/localhost/zones/" + name), | |
459 | data=json.dumps({'dnssec': False})) | |
460 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
461 | ||
462 | zoneinfo = r.json() | |
463 | ||
464 | self.assertEquals(r.status_code, 200) | |
465 | self.assertEquals(zoneinfo['dnssec'], False) | |
466 | ||
467 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/cryptokeys')) | |
468 | ||
469 | keys = r.json() | |
470 | ||
471 | self.assertEquals(r.status_code, 200) | |
472 | self.assertEquals(len(keys), 0) | |
473 | ||
986e4858 PL |
474 | def test_create_zone_with_nsec3param(self): |
475 | """ | |
476 | Create a zone with "nsec3param" set and see if the metadata was added. | |
477 | """ | |
478 | name = unique_zone_name() | |
479 | nsec3param = '1 0 500 aabbccddeeff' | |
480 | name, payload, data = self.create_zone(dnssec=True, nsec3param=nsec3param) | |
481 | ||
482 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
483 | ||
484 | for k in ('dnssec', 'nsec3param'): | |
485 | self.assertIn(k, data) | |
486 | if k in payload: | |
487 | self.assertEquals(data[k], payload[k]) | |
488 | ||
489 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/metadata/NSEC3PARAM')) | |
490 | ||
491 | data = r.json() | |
492 | ||
541bb91b | 493 | print(data) |
986e4858 PL |
494 | |
495 | self.assertEquals(r.status_code, 200) | |
496 | self.assertEquals(len(data['metadata']), 1) | |
497 | self.assertEquals(data['kind'], 'NSEC3PARAM') | |
498 | self.assertEquals(data['metadata'][0], nsec3param) | |
499 | ||
500 | def test_create_zone_with_nsec3narrow(self): | |
501 | """ | |
502 | Create a zone with "nsec3narrow" set and see if the metadata was added. | |
503 | """ | |
504 | name = unique_zone_name() | |
505 | nsec3param = '1 0 500 aabbccddeeff' | |
506 | name, payload, data = self.create_zone(dnssec=True, nsec3param=nsec3param, | |
507 | nsec3narrow=True) | |
508 | ||
509 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
510 | ||
511 | for k in ('dnssec', 'nsec3param', 'nsec3narrow'): | |
512 | self.assertIn(k, data) | |
513 | if k in payload: | |
514 | self.assertEquals(data[k], payload[k]) | |
515 | ||
516 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name + '/metadata/NSEC3NARROW')) | |
517 | ||
518 | data = r.json() | |
519 | ||
541bb91b | 520 | print(data) |
986e4858 PL |
521 | |
522 | self.assertEquals(r.status_code, 200) | |
523 | self.assertEquals(len(data['metadata']), 1) | |
524 | self.assertEquals(data['kind'], 'NSEC3NARROW') | |
525 | self.assertEquals(data['metadata'][0], '1') | |
526 | ||
a843c67e KM |
527 | def test_create_zone_dnssec_serial(self): |
528 | """ | |
529 | Create a zone set/unset "dnssec" and see if the serial was increased | |
530 | after every step | |
531 | """ | |
532 | name = unique_zone_name() | |
533 | name, payload, data = self.create_zone() | |
534 | ||
535 | soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2] | |
536 | self.assertEquals(soa_serial[-2:], '01') | |
537 | ||
538 | self.session.put(self.url("/api/v1/servers/localhost/zones/" + name), | |
539 | data=json.dumps({'dnssec': True})) | |
540 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
541 | ||
542 | data = r.json() | |
543 | soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2] | |
544 | ||
545 | self.assertEquals(r.status_code, 200) | |
546 | self.assertEquals(soa_serial[-2:], '02') | |
547 | ||
548 | self.session.put(self.url("/api/v1/servers/localhost/zones/" + name), | |
549 | data=json.dumps({'dnssec': False})) | |
550 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)) | |
551 | ||
552 | data = r.json() | |
553 | soa_serial = get_first_rec(data, name, 'SOA')['content'].split(' ')[2] | |
554 | ||
555 | self.assertEquals(r.status_code, 200) | |
556 | self.assertEquals(soa_serial[-2:], '03') | |
557 | ||
16e25450 CH |
558 | def test_zone_absolute_url(self): |
559 | name, payload, data = self.create_zone() | |
560 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) | |
561 | rdata = r.json() | |
562 | print(rdata[0]) | |
563 | self.assertTrue(rdata[0]['url'].startswith('/api/v')) | |
564 | ||
24e11043 CJ |
565 | def test_create_zone_metadata(self): |
566 | payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]} | |
567 | r = self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"), | |
568 | data=json.dumps(payload_metadata)) | |
569 | rdata = r.json() | |
570 | self.assertEquals(r.status_code, 201) | |
571 | self.assertEquals(rdata["metadata"], payload_metadata["metadata"]) | |
572 | ||
573 | def test_create_zone_metadata_kind(self): | |
574 | payload_metadata = {"metadata": ["127.0.0.2"]} | |
575 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE"), | |
576 | data=json.dumps(payload_metadata)) | |
577 | rdata = r.json() | |
578 | self.assertEquals(r.status_code, 200) | |
579 | self.assertEquals(rdata["metadata"], payload_metadata["metadata"]) | |
580 | ||
581 | def test_create_protected_zone_metadata(self): | |
582 | # test whether it prevents modification of certain kinds | |
583 | for k in ("NSEC3NARROW", "NSEC3PARAM", "PRESIGNED", "LUA-AXFR-SCRIPT"): | |
584 | payload = {"metadata": ["FOO", "BAR"]} | |
585 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/%s" % k), | |
586 | data=json.dumps(payload)) | |
587 | self.assertEquals(r.status_code, 422) | |
588 | ||
589 | def test_retrieve_zone_metadata(self): | |
590 | payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]} | |
591 | self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"), | |
592 | data=json.dumps(payload_metadata)) | |
593 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/example.com/metadata")) | |
594 | rdata = r.json() | |
595 | self.assertEquals(r.status_code, 200) | |
596 | self.assertIn(payload_metadata, rdata) | |
597 | ||
598 | def test_delete_zone_metadata(self): | |
599 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE")) | |
600 | self.assertEquals(r.status_code, 200) | |
601 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE")) | |
602 | rdata = r.json() | |
603 | self.assertEquals(r.status_code, 200) | |
604 | self.assertEquals(rdata["metadata"], []) | |
605 | ||
9ac4e6d5 PL |
606 | def test_create_external_zone_metadata(self): |
607 | payload_metadata = {"metadata": ["My very important message"]} | |
608 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/X-MYMETA"), | |
609 | data=json.dumps(payload_metadata)) | |
610 | self.assertEquals(r.status_code, 200) | |
611 | rdata = r.json() | |
612 | self.assertEquals(rdata["metadata"], payload_metadata["metadata"]) | |
613 | ||
d38e81e6 PL |
614 | def test_create_metadata_in_non_existent_zone(self): |
615 | payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]} | |
616 | r = self.session.post(self.url("/api/v1/servers/localhost/zones/idonotexist.123.456.example./metadata"), | |
617 | data=json.dumps(payload_metadata)) | |
77bfe8de PL |
618 | self.assertEquals(r.status_code, 404) |
619 | # Note: errors should probably contain json (see #5988) | |
620 | # self.assertIn('Could not find domain ', r.json()['error']) | |
d38e81e6 | 621 | |
4bdff352 CH |
622 | def test_create_slave_zone(self): |
623 | # Test that nameservers can be absent for slave zones. | |
6754ef71 | 624 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
4bdff352 CH |
625 | for k in ('name', 'masters', 'kind'): |
626 | self.assertIn(k, data) | |
627 | self.assertEquals(data[k], payload[k]) | |
541bb91b CH |
628 | print("payload:", payload) |
629 | print("data:", data) | |
4de11a54 | 630 | # Because slave zones don't get a SOA, we need to test that they'll show up in the zone list. |
46d06a12 | 631 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
4de11a54 | 632 | zonelist = r.json() |
541bb91b | 633 | print("zonelist:", zonelist) |
4de11a54 CH |
634 | self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) |
635 | # Also test that fetching the zone works. | |
46d06a12 | 636 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])) |
4de11a54 | 637 | data = r.json() |
541bb91b | 638 | print("zone (fetched):", data) |
4de11a54 CH |
639 | for k in ('name', 'masters', 'kind'): |
640 | self.assertIn(k, data) | |
641 | self.assertEquals(data[k], payload[k]) | |
642 | self.assertEqual(data['serial'], 0) | |
6754ef71 | 643 | self.assertEqual(data['rrsets'], []) |
4de11a54 | 644 | |
e543cc8f CH |
645 | def test_find_zone_by_name(self): |
646 | name = 'foo/' + unique_zone_name() | |
647 | name, payload, data = self.create_zone(name=name) | |
648 | r = self.session.get(self.url("/api/v1/servers/localhost/zones?zone=" + name)) | |
649 | data = r.json() | |
650 | print(data) | |
651 | self.assertEquals(data[0]['name'], name) | |
652 | ||
4de11a54 | 653 | def test_delete_slave_zone(self): |
6754ef71 | 654 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
46d06a12 | 655 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data['id'])) |
4de11a54 | 656 | r.raise_for_status() |
4bdff352 | 657 | |
a426cb89 | 658 | def test_retrieve_slave_zone(self): |
6754ef71 | 659 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
541bb91b CH |
660 | print("payload:", payload) |
661 | print("data:", data) | |
46d06a12 | 662 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/axfr-retrieve")) |
a426cb89 | 663 | data = r.json() |
541bb91b | 664 | print("status for axfr-retrieve:", data) |
a426cb89 CH |
665 | self.assertEqual(data['result'], u'Added retrieval request for \'' + payload['name'] + |
666 | '\' from master 127.0.0.2') | |
667 | ||
668 | def test_notify_master_zone(self): | |
6754ef71 | 669 | name, payload, data = self.create_zone(kind='Master') |
541bb91b CH |
670 | print("payload:", payload) |
671 | print("data:", data) | |
46d06a12 | 672 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/notify")) |
a426cb89 | 673 | data = r.json() |
541bb91b | 674 | print("status for notify:", data) |
a426cb89 CH |
675 | self.assertEqual(data['result'], 'Notification queued') |
676 | ||
c1374bdb | 677 | def test_get_zone_with_symbols(self): |
6754ef71 | 678 | name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name()) |
3c3c006b | 679 | name = payload['name'] |
1d6b70f9 | 680 | zone_id = (name.replace('/', '=2F')) |
46d06a12 | 681 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)) |
c1374bdb | 682 | data = r.json() |
6bb25159 | 683 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'dnssec'): |
3c3c006b CH |
684 | self.assertIn(k, data) |
685 | if k in payload: | |
686 | self.assertEquals(data[k], payload[k]) | |
687 | ||
c1374bdb | 688 | def test_get_zone(self): |
46d06a12 | 689 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
05776d2f | 690 | domains = r.json() |
1d6b70f9 | 691 | example_com = [domain for domain in domains if domain['name'] == u'example.com.'][0] |
46d06a12 | 692 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + example_com['id'])) |
c1374bdb | 693 | self.assert_success_json(r) |
05776d2f CH |
694 | data = r.json() |
695 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'): | |
696 | self.assertIn(k, data) | |
1d6b70f9 | 697 | self.assertEquals(data['name'], 'example.com.') |
7c0ba3d2 | 698 | |
0f0e73fe MS |
699 | def test_import_zone_broken(self): |
700 | payload = {} | |
701 | payload['zone'] = """ | |
702 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571 | |
703 | flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 | |
704 | ;; WARNING: recursion requested but not available | |
705 | ||
706 | ;; OPT PSEUDOSECTION: | |
707 | ; EDNS: version: 0, flags:; udp: 1680 | |
708 | ;; QUESTION SECTION: | |
709 | ;powerdns.com. IN SOA | |
710 | ||
711 | ;; ANSWER SECTION: | |
712 | powerdns-broken.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
713 | powerdns-broken.com. 3600 IN NS powerdnssec2.ds9a.nl. | |
714 | powerdns-broken.com. 3600 IN AAAA 2001:888:2000:1d::2 | |
715 | powerdns-broken.com. 86400 IN A 82.94.213.34 | |
716 | powerdns-broken.com. 3600 IN MX 0 xs.powerdns.com. | |
717 | powerdns-broken.com. 3600 IN NS powerdnssec1.ds9a.nl. | |
718 | powerdns-broken.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
719 | """ | |
1d6b70f9 | 720 | payload['name'] = 'powerdns-broken.com.' |
0f0e73fe MS |
721 | payload['kind'] = 'Master' |
722 | payload['nameservers'] = [] | |
723 | r = self.session.post( | |
46d06a12 | 724 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
725 | data=json.dumps(payload), |
726 | headers={'content-type': 'application/json'}) | |
727 | self.assertEquals(r.status_code, 422) | |
728 | ||
1d6b70f9 CH |
729 | def test_import_zone_axfr_outofzone(self): |
730 | # Ensure we don't create out-of-zone records | |
731 | name = unique_zone_name() | |
732 | payload = {} | |
733 | payload['zone'] = """ | |
734 | NAME 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
735 | NAME 3600 IN NS powerdnssec2.ds9a.nl. | |
736 | example.org. 3600 IN AAAA 2001:888:2000:1d::2 | |
737 | NAME 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
738 | """.replace('NAME', name) | |
739 | payload['name'] = name | |
740 | payload['kind'] = 'Master' | |
741 | payload['nameservers'] = [] | |
742 | r = self.session.post( | |
743 | self.url("/api/v1/servers/localhost/zones"), | |
744 | data=json.dumps(payload), | |
745 | headers={'content-type': 'application/json'}) | |
746 | self.assertEquals(r.status_code, 422) | |
747 | self.assertEqual(r.json()['error'], 'RRset example.org. IN AAAA: Name is out of zone') | |
748 | ||
0f0e73fe MS |
749 | def test_import_zone_axfr(self): |
750 | payload = {} | |
751 | payload['zone'] = """ | |
752 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571 | |
753 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 | |
754 | ;; WARNING: recursion requested but not available | |
755 | ||
756 | ;; OPT PSEUDOSECTION: | |
757 | ; EDNS: version: 0, flags:; udp: 1680 | |
758 | ;; QUESTION SECTION: | |
759 | ;powerdns.com. IN SOA | |
760 | ||
761 | ;; ANSWER SECTION: | |
762 | powerdns.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
763 | powerdns.com. 3600 IN NS powerdnssec2.ds9a.nl. | |
764 | powerdns.com. 3600 IN AAAA 2001:888:2000:1d::2 | |
765 | powerdns.com. 86400 IN A 82.94.213.34 | |
766 | powerdns.com. 3600 IN MX 0 xs.powerdns.com. | |
767 | powerdns.com. 3600 IN NS powerdnssec1.ds9a.nl. | |
768 | powerdns.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
769 | """ | |
1d6b70f9 | 770 | payload['name'] = 'powerdns.com.' |
0f0e73fe MS |
771 | payload['kind'] = 'Master' |
772 | payload['nameservers'] = [] | |
1d6b70f9 | 773 | payload['soa_edit_api'] = '' # turn off so exact SOA comparison works. |
0f0e73fe | 774 | r = self.session.post( |
46d06a12 | 775 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
776 | data=json.dumps(payload), |
777 | headers={'content-type': 'application/json'}) | |
778 | self.assert_success_json(r) | |
779 | data = r.json() | |
780 | self.assertIn('name', data) | |
0f0e73fe | 781 | |
90568eb2 MS |
782 | expected = { |
783 | 'NS': [ | |
6754ef71 CH |
784 | {'content': 'powerdnssec1.ds9a.nl.'}, |
785 | {'content': 'powerdnssec2.ds9a.nl.'}, | |
786 | ], | |
90568eb2 | 787 | 'SOA': [ |
6754ef71 CH |
788 | {'content': 'powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800'}, |
789 | ], | |
90568eb2 | 790 | 'MX': [ |
6754ef71 CH |
791 | {'content': '0 xs.powerdns.com.'}, |
792 | ], | |
90568eb2 | 793 | 'A': [ |
6754ef71 CH |
794 | {'content': '82.94.213.34', 'name': 'powerdns.com.'}, |
795 | ], | |
90568eb2 | 796 | 'AAAA': [ |
6754ef71 CH |
797 | {'content': '2001:888:2000:1d::2', 'name': 'powerdns.com.'}, |
798 | ], | |
90568eb2 | 799 | } |
0f0e73fe | 800 | |
6754ef71 | 801 | eq_zone_rrsets(data['rrsets'], expected) |
1d6b70f9 | 802 | |
e3675a8a | 803 | # check content in DB is stored WITHOUT trailing dot. |
1d6b70f9 | 804 | dbrecs = get_db_records(payload['name'], 'NS') |
e3675a8a CH |
805 | dbrec = next((dbrec for dbrec in dbrecs if dbrec['content'].startswith('powerdnssec1'))) |
806 | self.assertEqual(dbrec['content'], 'powerdnssec1.ds9a.nl') | |
0f0e73fe MS |
807 | |
808 | def test_import_zone_bind(self): | |
809 | payload = {} | |
810 | payload['zone'] = """ | |
811 | $TTL 86400 ; 24 hours could have been written as 24h or 1d | |
812 | ; $TTL used for all RRs without explicit TTL value | |
813 | $ORIGIN example.org. | |
814 | @ 1D IN SOA ns1.example.org. hostmaster.example.org. ( | |
815 | 2002022401 ; serial | |
816 | 3H ; refresh | |
817 | 15 ; retry | |
818 | 1w ; expire | |
819 | 3h ; minimum | |
820 | ) | |
821 | IN NS ns1.example.org. ; in the domain | |
822 | IN NS ns2.smokeyjoe.com. ; external to domain | |
823 | IN MX 10 mail.another.com. ; external mail provider | |
824 | ; server host definitions | |
1d6b70f9 | 825 | ns1 IN A 192.168.0.1 ;name server definition |
0f0e73fe MS |
826 | www IN A 192.168.0.2 ;web server definition |
827 | ftp IN CNAME www.example.org. ;ftp server definition | |
828 | ; non server domain hosts | |
829 | bill IN A 192.168.0.3 | |
1d6b70f9 | 830 | fred IN A 192.168.0.4 |
0f0e73fe | 831 | """ |
1d6b70f9 | 832 | payload['name'] = 'example.org.' |
0f0e73fe MS |
833 | payload['kind'] = 'Master' |
834 | payload['nameservers'] = [] | |
1d6b70f9 | 835 | payload['soa_edit_api'] = '' # turn off so exact SOA comparison works. |
0f0e73fe | 836 | r = self.session.post( |
46d06a12 | 837 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
838 | data=json.dumps(payload), |
839 | headers={'content-type': 'application/json'}) | |
840 | self.assert_success_json(r) | |
841 | data = r.json() | |
842 | self.assertIn('name', data) | |
0f0e73fe | 843 | |
90568eb2 MS |
844 | expected = { |
845 | 'NS': [ | |
6754ef71 CH |
846 | {'content': 'ns1.example.org.'}, |
847 | {'content': 'ns2.smokeyjoe.com.'}, | |
848 | ], | |
90568eb2 | 849 | 'SOA': [ |
6754ef71 CH |
850 | {'content': 'ns1.example.org. hostmaster.example.org. 2002022401 10800 15 604800 10800'}, |
851 | ], | |
90568eb2 | 852 | 'MX': [ |
6754ef71 CH |
853 | {'content': '10 mail.another.com.'}, |
854 | ], | |
90568eb2 | 855 | 'A': [ |
6754ef71 CH |
856 | {'content': '192.168.0.1', 'name': 'ns1.example.org.'}, |
857 | {'content': '192.168.0.2', 'name': 'www.example.org.'}, | |
858 | {'content': '192.168.0.3', 'name': 'bill.example.org.'}, | |
859 | {'content': '192.168.0.4', 'name': 'fred.example.org.'}, | |
860 | ], | |
90568eb2 | 861 | 'CNAME': [ |
6754ef71 CH |
862 | {'content': 'www.example.org.', 'name': 'ftp.example.org.'}, |
863 | ], | |
90568eb2 | 864 | } |
0f0e73fe | 865 | |
6754ef71 | 866 | eq_zone_rrsets(data['rrsets'], expected) |
0f0e73fe | 867 | |
c1374bdb | 868 | def test_export_zone_json(self): |
6754ef71 | 869 | name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='') |
a83004d3 CH |
870 | # export it |
871 | r = self.session.get( | |
46d06a12 | 872 | self.url("/api/v1/servers/localhost/zones/" + name + "/export"), |
a83004d3 CH |
873 | headers={'accept': 'application/json;q=0.9,*/*;q=0.8'} |
874 | ) | |
c1374bdb | 875 | self.assert_success_json(r) |
a83004d3 CH |
876 | data = r.json() |
877 | self.assertIn('zone', data) | |
ba2a1254 DK |
878 | expected_data = [name + '\t3600\tIN\tNS\tns1.foo.com.', |
879 | name + '\t3600\tIN\tNS\tns2.foo.com.', | |
880 | name + '\t3600\tIN\tSOA\ta.misconfigured.powerdns.server. hostmaster.' + name + | |
1d6b70f9 | 881 | ' 0 10800 3600 604800 3600'] |
a83004d3 CH |
882 | self.assertEquals(data['zone'].strip().split('\n'), expected_data) |
883 | ||
c1374bdb | 884 | def test_export_zone_text(self): |
6754ef71 | 885 | name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='') |
a83004d3 CH |
886 | # export it |
887 | r = self.session.get( | |
46d06a12 | 888 | self.url("/api/v1/servers/localhost/zones/" + name + "/export"), |
a83004d3 CH |
889 | headers={'accept': '*/*'} |
890 | ) | |
891 | data = r.text.strip().split("\n") | |
ba2a1254 DK |
892 | expected_data = [name + '\t3600\tIN\tNS\tns1.foo.com.', |
893 | name + '\t3600\tIN\tNS\tns2.foo.com.', | |
894 | name + '\t3600\tIN\tSOA\ta.misconfigured.powerdns.server. hostmaster.' + name + | |
1d6b70f9 | 895 | ' 0 10800 3600 604800 3600'] |
a83004d3 CH |
896 | self.assertEquals(data, expected_data) |
897 | ||
c1374bdb | 898 | def test_update_zone(self): |
6754ef71 | 899 | name, payload, zone = self.create_zone() |
bee2acae | 900 | name = payload['name'] |
d29d5db7 | 901 | # update, set as Master and enable SOA-EDIT-API |
7c0ba3d2 CH |
902 | payload = { |
903 | 'kind': 'Master', | |
c1374bdb | 904 | 'masters': ['192.0.2.1', '192.0.2.2'], |
6bb25159 MS |
905 | 'soa_edit_api': 'EPOCH', |
906 | 'soa_edit': 'EPOCH' | |
7c0ba3d2 CH |
907 | } |
908 | r = self.session.put( | |
46d06a12 | 909 | self.url("/api/v1/servers/localhost/zones/" + name), |
7c0ba3d2 CH |
910 | data=json.dumps(payload), |
911 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
912 | self.assert_success(r) |
913 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() | |
7c0ba3d2 CH |
914 | for k in payload.keys(): |
915 | self.assertIn(k, data) | |
916 | self.assertEquals(data[k], payload[k]) | |
d29d5db7 | 917 | # update, back to Native and empty(off) |
7c0ba3d2 | 918 | payload = { |
d29d5db7 | 919 | 'kind': 'Native', |
6bb25159 MS |
920 | 'soa_edit_api': '', |
921 | 'soa_edit': '' | |
7c0ba3d2 CH |
922 | } |
923 | r = self.session.put( | |
46d06a12 | 924 | self.url("/api/v1/servers/localhost/zones/" + name), |
7c0ba3d2 CH |
925 | data=json.dumps(payload), |
926 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
927 | self.assert_success(r) |
928 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() | |
7c0ba3d2 CH |
929 | for k in payload.keys(): |
930 | self.assertIn(k, data) | |
931 | self.assertEquals(data[k], payload[k]) | |
b3905a3d | 932 | |
c1374bdb | 933 | def test_zone_rr_update(self): |
6754ef71 | 934 | name, payload, zone = self.create_zone() |
b3905a3d | 935 | # do a replace (= update) |
d708640f | 936 | rrset = { |
b3905a3d CH |
937 | 'changetype': 'replace', |
938 | 'name': name, | |
8ce0dc75 | 939 | 'type': 'ns', |
6754ef71 | 940 | 'ttl': 3600, |
b3905a3d CH |
941 | 'records': [ |
942 | { | |
1d6b70f9 | 943 | "content": "ns1.bar.com.", |
cea26350 CH |
944 | "disabled": False |
945 | }, | |
946 | { | |
1d6b70f9 | 947 | "content": "ns2-disabled.bar.com.", |
cea26350 | 948 | "disabled": True |
b3905a3d CH |
949 | } |
950 | ] | |
951 | } | |
d708640f | 952 | payload = {'rrsets': [rrset]} |
b3905a3d | 953 | r = self.session.patch( |
46d06a12 | 954 | self.url("/api/v1/servers/localhost/zones/" + name), |
b3905a3d CH |
955 | data=json.dumps(payload), |
956 | headers={'content-type': 'application/json'}) | |
f0e76cee | 957 | self.assert_success(r) |
b3905a3d | 958 | # verify that (only) the new record is there |
f0e76cee | 959 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 960 | self.assertEquals(get_rrset(data, name, 'NS')['records'], rrset['records']) |
b3905a3d | 961 | |
c1374bdb | 962 | def test_zone_rr_update_mx(self): |
05cf6a71 | 963 | # Important to test with MX records, as they have a priority field, which must end up in the content field. |
6754ef71 | 964 | name, payload, zone = self.create_zone() |
41e3b10e | 965 | # do a replace (= update) |
d708640f | 966 | rrset = { |
41e3b10e CH |
967 | 'changetype': 'replace', |
968 | 'name': name, | |
969 | 'type': 'MX', | |
6754ef71 | 970 | 'ttl': 3600, |
41e3b10e CH |
971 | 'records': [ |
972 | { | |
1d6b70f9 | 973 | "content": "10 mail.example.org.", |
41e3b10e CH |
974 | "disabled": False |
975 | } | |
976 | ] | |
977 | } | |
d708640f | 978 | payload = {'rrsets': [rrset]} |
41e3b10e | 979 | r = self.session.patch( |
46d06a12 | 980 | self.url("/api/v1/servers/localhost/zones/" + name), |
41e3b10e CH |
981 | data=json.dumps(payload), |
982 | headers={'content-type': 'application/json'}) | |
f0e76cee | 983 | self.assert_success(r) |
41e3b10e | 984 | # verify that (only) the new record is there |
f0e76cee | 985 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 986 | self.assertEquals(get_rrset(data, name, 'MX')['records'], rrset['records']) |
d708640f | 987 | |
81950930 CHB |
988 | def test_zone_rr_update_invalid_mx(self): |
989 | name, payload, zone = self.create_zone() | |
990 | # do a replace (= update) | |
991 | rrset = { | |
992 | 'changetype': 'replace', | |
993 | 'name': name, | |
994 | 'type': 'MX', | |
995 | 'ttl': 3600, | |
996 | 'records': [ | |
997 | { | |
998 | "content": "10 mail@mx.example.org.", | |
999 | "disabled": False | |
1000 | } | |
1001 | ] | |
1002 | } | |
1003 | payload = {'rrsets': [rrset]} | |
1004 | r = self.session.patch( | |
1005 | self.url("/api/v1/servers/localhost/zones/" + name), | |
1006 | data=json.dumps(payload), | |
1007 | headers={'content-type': 'application/json'}) | |
1008 | self.assertEquals(r.status_code, 422) | |
97c8ea81 | 1009 | self.assertIn('non-hostname content', r.json()['error']) |
81950930 CHB |
1010 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1011 | self.assertIsNone(get_rrset(data, name, 'MX')) | |
1012 | ||
a53b24d0 CHB |
1013 | def test_zone_rr_update_opt(self): |
1014 | name, payload, zone = self.create_zone() | |
1015 | # do a replace (= update) | |
1016 | rrset = { | |
1017 | 'changetype': 'replace', | |
1018 | 'name': name, | |
1019 | 'type': 'OPT', | |
1020 | 'ttl': 3600, | |
1021 | 'records': [ | |
1022 | { | |
1023 | "content": "9", | |
1024 | "disabled": False | |
1025 | } | |
1026 | ] | |
1027 | } | |
1028 | payload = {'rrsets': [rrset]} | |
1029 | r = self.session.patch( | |
1030 | self.url("/api/v1/servers/localhost/zones/" + name), | |
1031 | data=json.dumps(payload), | |
1032 | headers={'content-type': 'application/json'}) | |
1033 | self.assertEquals(r.status_code, 422) | |
1034 | self.assertIn('OPT: invalid type given', r.json()['error']) | |
1035 | ||
c1374bdb | 1036 | def test_zone_rr_update_multiple_rrsets(self): |
6754ef71 | 1037 | name, payload, zone = self.create_zone() |
d708640f CH |
1038 | rrset1 = { |
1039 | 'changetype': 'replace', | |
1040 | 'name': name, | |
1041 | 'type': 'NS', | |
6754ef71 | 1042 | 'ttl': 3600, |
d708640f CH |
1043 | 'records': [ |
1044 | { | |
6754ef71 | 1045 | |
1d6b70f9 | 1046 | "content": "ns9999.example.com.", |
d708640f CH |
1047 | "disabled": False |
1048 | } | |
1049 | ] | |
1050 | } | |
1051 | rrset2 = { | |
1052 | 'changetype': 'replace', | |
1053 | 'name': name, | |
1054 | 'type': 'MX', | |
6754ef71 | 1055 | 'ttl': 3600, |
d708640f CH |
1056 | 'records': [ |
1057 | { | |
1d6b70f9 | 1058 | "content": "10 mx444.example.com.", |
d708640f CH |
1059 | "disabled": False |
1060 | } | |
1061 | ] | |
1062 | } | |
1063 | payload = {'rrsets': [rrset1, rrset2]} | |
1064 | r = self.session.patch( | |
46d06a12 | 1065 | self.url("/api/v1/servers/localhost/zones/" + name), |
d708640f CH |
1066 | data=json.dumps(payload), |
1067 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1068 | self.assert_success(r) |
d708640f | 1069 | # verify that all rrsets have been updated |
f0e76cee | 1070 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 CH |
1071 | self.assertEquals(get_rrset(data, name, 'NS')['records'], rrset1['records']) |
1072 | self.assertEquals(get_rrset(data, name, 'MX')['records'], rrset2['records']) | |
41e3b10e | 1073 | |
e3675a8a CH |
1074 | def test_zone_rr_update_duplicate_record(self): |
1075 | name, payload, zone = self.create_zone() | |
1076 | rrset = { | |
1077 | 'changetype': 'replace', | |
1078 | 'name': name, | |
1079 | 'type': 'NS', | |
1080 | 'ttl': 3600, | |
1081 | 'records': [ | |
1082 | {"content": "ns9999.example.com.", "disabled": False}, | |
1083 | {"content": "ns9996.example.com.", "disabled": False}, | |
1084 | {"content": "ns9987.example.com.", "disabled": False}, | |
1085 | {"content": "ns9988.example.com.", "disabled": False}, | |
1086 | {"content": "ns9999.example.com.", "disabled": False}, | |
1087 | ] | |
1088 | } | |
1089 | payload = {'rrsets': [rrset]} | |
1090 | r = self.session.patch( | |
1091 | self.url("/api/v1/servers/localhost/zones/" + name), | |
1092 | data=json.dumps(payload), | |
1093 | headers={'content-type': 'application/json'}) | |
1094 | self.assertEquals(r.status_code, 422) | |
1095 | self.assertIn('Duplicate record in RRset', r.json()['error']) | |
1096 | ||
90904988 PD |
1097 | def test_zone_rr_update_duplicate_rrset(self): |
1098 | name, payload, zone = self.create_zone() | |
1099 | rrset1 = { | |
1100 | 'changetype': 'replace', | |
1101 | 'name': name, | |
1102 | 'type': 'NS', | |
1103 | 'ttl': 3600, | |
1104 | 'records': [ | |
1105 | { | |
1106 | "content": "ns9999.example.com.", | |
1107 | "disabled": False | |
1108 | } | |
1109 | ] | |
1110 | } | |
1111 | rrset2 = { | |
1112 | 'changetype': 'replace', | |
1113 | 'name': name, | |
1114 | 'type': 'NS', | |
1115 | 'ttl': 3600, | |
1116 | 'records': [ | |
1117 | { | |
1118 | "content": "ns9998.example.com.", | |
1119 | "disabled": False | |
1120 | } | |
1121 | ] | |
1122 | } | |
1123 | payload = {'rrsets': [rrset1, rrset2]} | |
1124 | r = self.session.patch( | |
1125 | self.url("/api/v1/servers/localhost/zones/" + name), | |
1126 | data=json.dumps(payload), | |
1127 | headers={'content-type': 'application/json'}) | |
1128 | self.assertEquals(r.status_code, 422) | |
1129 | self.assertIn('Duplicate RRset', r.json()['error']) | |
1130 | ||
c1374bdb | 1131 | def test_zone_rr_delete(self): |
6754ef71 | 1132 | name, payload, zone = self.create_zone() |
b3905a3d | 1133 | # do a delete of all NS records (these are created with the zone) |
d708640f | 1134 | rrset = { |
b3905a3d CH |
1135 | 'changetype': 'delete', |
1136 | 'name': name, | |
1137 | 'type': 'NS' | |
1138 | } | |
d708640f | 1139 | payload = {'rrsets': [rrset]} |
b3905a3d | 1140 | r = self.session.patch( |
46d06a12 | 1141 | self.url("/api/v1/servers/localhost/zones/" + name), |
b3905a3d CH |
1142 | data=json.dumps(payload), |
1143 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1144 | self.assert_success(r) |
b3905a3d | 1145 | # verify that the records are gone |
f0e76cee | 1146 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 1147 | self.assertIsNone(get_rrset(data, name, 'NS')) |
cea26350 | 1148 | |
c1374bdb | 1149 | def test_zone_disable_reenable(self): |
d29d5db7 | 1150 | # This also tests that SOA-EDIT-API works. |
6754ef71 | 1151 | name, payload, zone = self.create_zone(soa_edit_api='EPOCH') |
cea26350 | 1152 | # disable zone by disabling SOA |
d708640f | 1153 | rrset = { |
cea26350 CH |
1154 | 'changetype': 'replace', |
1155 | 'name': name, | |
1156 | 'type': 'SOA', | |
6754ef71 | 1157 | 'ttl': 3600, |
cea26350 CH |
1158 | 'records': [ |
1159 | { | |
1d6b70f9 | 1160 | "content": "ns1.bar.com. hostmaster.foo.org. 1 1 1 1 1", |
cea26350 CH |
1161 | "disabled": True |
1162 | } | |
1163 | ] | |
1164 | } | |
d708640f | 1165 | payload = {'rrsets': [rrset]} |
cea26350 | 1166 | r = self.session.patch( |
46d06a12 | 1167 | self.url("/api/v1/servers/localhost/zones/" + name), |
cea26350 CH |
1168 | data=json.dumps(payload), |
1169 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1170 | self.assert_success(r) |
d29d5db7 | 1171 | # check SOA serial has been edited |
f0e76cee CH |
1172 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1173 | soa_serial1 = get_first_rec(data, name, 'SOA')['content'].split()[2] | |
d29d5db7 CH |
1174 | self.assertNotEquals(soa_serial1, '1') |
1175 | # make sure domain is still in zone list (disabled SOA!) | |
46d06a12 | 1176 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
cea26350 CH |
1177 | domains = r.json() |
1178 | self.assertEquals(len([domain for domain in domains if domain['name'] == name]), 1) | |
d29d5db7 CH |
1179 | # sleep 1sec to ensure the EPOCH value changes for the next request |
1180 | time.sleep(1) | |
cea26350 | 1181 | # verify that modifying it still works |
d708640f CH |
1182 | rrset['records'][0]['disabled'] = False |
1183 | payload = {'rrsets': [rrset]} | |
cea26350 | 1184 | r = self.session.patch( |
46d06a12 | 1185 | self.url("/api/v1/servers/localhost/zones/" + name), |
cea26350 CH |
1186 | data=json.dumps(payload), |
1187 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1188 | self.assert_success(r) |
d29d5db7 | 1189 | # check SOA serial has been edited again |
f0e76cee CH |
1190 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1191 | soa_serial2 = get_first_rec(data, name, 'SOA')['content'].split()[2] | |
d29d5db7 CH |
1192 | self.assertNotEquals(soa_serial2, '1') |
1193 | self.assertNotEquals(soa_serial2, soa_serial1) | |
02945d9a | 1194 | |
c1374bdb | 1195 | def test_zone_rr_update_out_of_zone(self): |
6754ef71 | 1196 | name, payload, zone = self.create_zone() |
35f26cc5 | 1197 | # replace with qname mismatch |
d708640f | 1198 | rrset = { |
35f26cc5 | 1199 | 'changetype': 'replace', |
1d6b70f9 | 1200 | 'name': 'not-in-zone.', |
35f26cc5 | 1201 | 'type': 'NS', |
6754ef71 | 1202 | 'ttl': 3600, |
35f26cc5 CH |
1203 | 'records': [ |
1204 | { | |
1d6b70f9 | 1205 | "content": "ns1.bar.com.", |
35f26cc5 CH |
1206 | "disabled": False |
1207 | } | |
1208 | ] | |
1209 | } | |
d708640f | 1210 | payload = {'rrsets': [rrset]} |
35f26cc5 | 1211 | r = self.session.patch( |
46d06a12 | 1212 | self.url("/api/v1/servers/localhost/zones/" + name), |
35f26cc5 CH |
1213 | data=json.dumps(payload), |
1214 | headers={'content-type': 'application/json'}) | |
1215 | self.assertEquals(r.status_code, 422) | |
1216 | self.assertIn('out of zone', r.json()['error']) | |
1217 | ||
1d6b70f9 | 1218 | def test_zone_rr_update_restricted_chars(self): |
6754ef71 | 1219 | name, payload, zone = self.create_zone() |
1d6b70f9 CH |
1220 | # replace with qname mismatch |
1221 | rrset = { | |
1222 | 'changetype': 'replace', | |
1223 | 'name': 'test:' + name, | |
1224 | 'type': 'NS', | |
6754ef71 | 1225 | 'ttl': 3600, |
1d6b70f9 CH |
1226 | 'records': [ |
1227 | { | |
1d6b70f9 CH |
1228 | "content": "ns1.bar.com.", |
1229 | "disabled": False | |
1230 | } | |
1231 | ] | |
1232 | } | |
1233 | payload = {'rrsets': [rrset]} | |
1234 | r = self.session.patch( | |
1235 | self.url("/api/v1/servers/localhost/zones/" + name), | |
1236 | data=json.dumps(payload), | |
1237 | headers={'content-type': 'application/json'}) | |
1238 | self.assertEquals(r.status_code, 422) | |
1239 | self.assertIn('contains unsupported characters', r.json()['error']) | |
1240 | ||
24cd86ca | 1241 | def test_rrset_unknown_type(self): |
6754ef71 | 1242 | name, payload, zone = self.create_zone() |
24cd86ca CH |
1243 | rrset = { |
1244 | 'changetype': 'replace', | |
1245 | 'name': name, | |
1246 | 'type': 'FAFAFA', | |
6754ef71 | 1247 | 'ttl': 3600, |
24cd86ca CH |
1248 | 'records': [ |
1249 | { | |
24cd86ca CH |
1250 | "content": "4.3.2.1", |
1251 | "disabled": False | |
1252 | } | |
1253 | ] | |
1254 | } | |
1255 | payload = {'rrsets': [rrset]} | |
46d06a12 | 1256 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), |
24cd86ca CH |
1257 | headers={'content-type': 'application/json'}) |
1258 | self.assertEquals(r.status_code, 422) | |
1259 | self.assertIn('unknown type', r.json()['error']) | |
1260 | ||
8560f36a CH |
1261 | def test_rrset_cname_and_other(self): |
1262 | name, payload, zone = self.create_zone() | |
1263 | rrset = { | |
1264 | 'changetype': 'replace', | |
1265 | 'name': name, | |
1266 | 'type': 'CNAME', | |
1267 | 'ttl': 3600, | |
1268 | 'records': [ | |
1269 | { | |
1270 | "content": "example.org.", | |
1271 | "disabled": False | |
1272 | } | |
1273 | ] | |
1274 | } | |
1275 | payload = {'rrsets': [rrset]} | |
1276 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), | |
1277 | headers={'content-type': 'application/json'}) | |
1278 | self.assertEquals(r.status_code, 422) | |
1279 | self.assertIn('Conflicts with pre-existing non-CNAME RRset', r.json()['error']) | |
1280 | ||
1281 | def test_rrset_other_and_cname(self): | |
1282 | name, payload, zone = self.create_zone() | |
1283 | rrset = { | |
1284 | 'changetype': 'replace', | |
1285 | 'name': 'sub.'+name, | |
1286 | 'type': 'CNAME', | |
1287 | 'ttl': 3600, | |
1288 | 'records': [ | |
1289 | { | |
1290 | "content": "example.org.", | |
1291 | "disabled": False | |
1292 | } | |
1293 | ] | |
1294 | } | |
1295 | payload = {'rrsets': [rrset]} | |
1296 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), | |
1297 | headers={'content-type': 'application/json'}) | |
1298 | self.assert_success(r) | |
1299 | rrset = { | |
1300 | 'changetype': 'replace', | |
1301 | 'name': 'sub.'+name, | |
1302 | 'type': 'A', | |
1303 | 'ttl': 3600, | |
1304 | 'records': [ | |
1305 | { | |
1306 | "content": "1.2.3.4", | |
1307 | "disabled": False | |
1308 | } | |
1309 | ] | |
1310 | } | |
1311 | payload = {'rrsets': [rrset]} | |
1312 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), | |
1313 | headers={'content-type': 'application/json'}) | |
1314 | self.assertEquals(r.status_code, 422) | |
1315 | self.assertIn('Conflicts with pre-existing CNAME RRset', r.json()['error']) | |
1316 | ||
1e5b9ab9 CH |
1317 | def test_create_zone_with_leading_space(self): |
1318 | # Actual regression. | |
6754ef71 | 1319 | name, payload, zone = self.create_zone() |
1e5b9ab9 CH |
1320 | rrset = { |
1321 | 'changetype': 'replace', | |
1322 | 'name': name, | |
1323 | 'type': 'A', | |
6754ef71 | 1324 | 'ttl': 3600, |
1e5b9ab9 CH |
1325 | 'records': [ |
1326 | { | |
1e5b9ab9 CH |
1327 | "content": " 4.3.2.1", |
1328 | "disabled": False | |
1329 | } | |
1330 | ] | |
1331 | } | |
1332 | payload = {'rrsets': [rrset]} | |
46d06a12 | 1333 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), |
1e5b9ab9 CH |
1334 | headers={'content-type': 'application/json'}) |
1335 | self.assertEquals(r.status_code, 422) | |
1336 | self.assertIn('Not in expected format', r.json()['error']) | |
1337 | ||
c1374bdb | 1338 | def test_zone_rr_delete_out_of_zone(self): |
6754ef71 | 1339 | name, payload, zone = self.create_zone() |
d708640f | 1340 | rrset = { |
35f26cc5 | 1341 | 'changetype': 'delete', |
1d6b70f9 | 1342 | 'name': 'not-in-zone.', |
35f26cc5 CH |
1343 | 'type': 'NS' |
1344 | } | |
d708640f | 1345 | payload = {'rrsets': [rrset]} |
35f26cc5 | 1346 | r = self.session.patch( |
46d06a12 | 1347 | self.url("/api/v1/servers/localhost/zones/" + name), |
35f26cc5 CH |
1348 | data=json.dumps(payload), |
1349 | headers={'content-type': 'application/json'}) | |
541bb91b | 1350 | print(r.content) |
f0e76cee | 1351 | self.assert_success(r) # succeed so users can fix their wrong, old data |
35f26cc5 | 1352 | |
37663c3b | 1353 | def test_zone_delete(self): |
6754ef71 | 1354 | name, payload, zone = self.create_zone() |
46d06a12 | 1355 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) |
37663c3b CH |
1356 | self.assertEquals(r.status_code, 204) |
1357 | self.assertNotIn('Content-Type', r.headers) | |
1358 | ||
c1374bdb | 1359 | def test_zone_comment_create(self): |
6754ef71 | 1360 | name, payload, zone = self.create_zone() |
d708640f | 1361 | rrset = { |
6cc98ddf CH |
1362 | 'changetype': 'replace', |
1363 | 'name': name, | |
1364 | 'type': 'NS', | |
6754ef71 | 1365 | 'ttl': 3600, |
6cc98ddf CH |
1366 | 'comments': [ |
1367 | { | |
1368 | 'account': 'test1', | |
1369 | 'content': 'blah blah', | |
1370 | }, | |
1371 | { | |
1372 | 'account': 'test2', | |
1373 | 'content': 'blah blah bleh', | |
1374 | } | |
1375 | ] | |
1376 | } | |
d708640f | 1377 | payload = {'rrsets': [rrset]} |
6cc98ddf | 1378 | r = self.session.patch( |
46d06a12 | 1379 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
1380 | data=json.dumps(payload), |
1381 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1382 | self.assert_success(r) |
6cc98ddf CH |
1383 | # make sure the comments have been set, and that the NS |
1384 | # records are still present | |
f0e76cee CH |
1385 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1386 | serverset = get_rrset(data, name, 'NS') | |
541bb91b | 1387 | print(serverset) |
6754ef71 CH |
1388 | self.assertNotEquals(serverset['records'], []) |
1389 | self.assertNotEquals(serverset['comments'], []) | |
6cc98ddf | 1390 | # verify that modified_at has been set by pdns |
6754ef71 | 1391 | self.assertNotEquals([c for c in serverset['comments']][0]['modified_at'], 0) |
0d7f3c75 CH |
1392 | # verify that TTL is correct (regression test) |
1393 | self.assertEquals(serverset['ttl'], 3600) | |
6cc98ddf | 1394 | |
c1374bdb | 1395 | def test_zone_comment_delete(self): |
6cc98ddf | 1396 | # Test: Delete ONLY comments. |
6754ef71 | 1397 | name, payload, zone = self.create_zone() |
d708640f | 1398 | rrset = { |
6cc98ddf CH |
1399 | 'changetype': 'replace', |
1400 | 'name': name, | |
1401 | 'type': 'NS', | |
1402 | 'comments': [] | |
1403 | } | |
d708640f | 1404 | payload = {'rrsets': [rrset]} |
6cc98ddf | 1405 | r = self.session.patch( |
46d06a12 | 1406 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
1407 | data=json.dumps(payload), |
1408 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1409 | self.assert_success(r) |
6cc98ddf | 1410 | # make sure the NS records are still present |
f0e76cee CH |
1411 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1412 | serverset = get_rrset(data, name, 'NS') | |
541bb91b | 1413 | print(serverset) |
6754ef71 CH |
1414 | self.assertNotEquals(serverset['records'], []) |
1415 | self.assertEquals(serverset['comments'], []) | |
6cc98ddf | 1416 | |
c1374bdb | 1417 | def test_zone_comment_stay_intact(self): |
6cc98ddf | 1418 | # Test if comments on an rrset stay intact if the rrset is replaced |
6754ef71 | 1419 | name, payload, zone = self.create_zone() |
6cc98ddf | 1420 | # create a comment |
d708640f | 1421 | rrset = { |
6cc98ddf CH |
1422 | 'changetype': 'replace', |
1423 | 'name': name, | |
1424 | 'type': 'NS', | |
1425 | 'comments': [ | |
1426 | { | |
1427 | 'account': 'test1', | |
1428 | 'content': 'oh hi there', | |
2696eea0 | 1429 | 'modified_at': 1111 |
6cc98ddf CH |
1430 | } |
1431 | ] | |
1432 | } | |
d708640f | 1433 | payload = {'rrsets': [rrset]} |
6cc98ddf | 1434 | r = self.session.patch( |
46d06a12 | 1435 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
1436 | data=json.dumps(payload), |
1437 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1438 | self.assert_success(r) |
6cc98ddf | 1439 | # replace rrset records |
d708640f | 1440 | rrset2 = { |
6cc98ddf CH |
1441 | 'changetype': 'replace', |
1442 | 'name': name, | |
1443 | 'type': 'NS', | |
6754ef71 | 1444 | 'ttl': 3600, |
6cc98ddf CH |
1445 | 'records': [ |
1446 | { | |
1d6b70f9 | 1447 | "content": "ns1.bar.com.", |
6cc98ddf CH |
1448 | "disabled": False |
1449 | } | |
1450 | ] | |
1451 | } | |
d708640f | 1452 | payload2 = {'rrsets': [rrset2]} |
6cc98ddf | 1453 | r = self.session.patch( |
46d06a12 | 1454 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
1455 | data=json.dumps(payload2), |
1456 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1457 | self.assert_success(r) |
6cc98ddf | 1458 | # make sure the comments still exist |
f0e76cee CH |
1459 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
1460 | serverset = get_rrset(data, name, 'NS') | |
541bb91b | 1461 | print(serverset) |
6754ef71 CH |
1462 | self.assertEquals(serverset['records'], rrset2['records']) |
1463 | self.assertEquals(serverset['comments'], rrset['comments']) | |
6cc98ddf | 1464 | |
3fe7c7d6 CH |
1465 | def test_zone_auto_ptr_ipv4_create(self): |
1466 | revzone = '4.2.192.in-addr.arpa.' | |
1467 | _, _, revzonedata = self.create_zone(name=revzone) | |
1468 | name = unique_zone_name() | |
1469 | rrset = { | |
1470 | "name": name, | |
1471 | "type": "A", | |
1472 | "ttl": 3600, | |
1473 | "records": [{ | |
1474 | "content": "192.2.4.44", | |
1475 | "disabled": False, | |
1476 | "set-ptr": True, | |
1477 | }], | |
1478 | } | |
1479 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
1480 | del rrset['records'][0]['set-ptr'] | |
1481 | self.assertEquals(get_rrset(data, name, 'A')['records'], rrset['records']) | |
1482 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() | |
1483 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
541bb91b | 1484 | print(revsets) |
3fe7c7d6 CH |
1485 | self.assertEquals(revsets, [{ |
1486 | u'name': u'44.4.2.192.in-addr.arpa.', | |
1487 | u'ttl': 3600, | |
1488 | u'type': u'PTR', | |
1489 | u'comments': [], | |
1490 | u'records': [{ | |
1491 | u'content': name, | |
1492 | u'disabled': False, | |
1493 | }], | |
1494 | }]) | |
1495 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. | |
1496 | self.assertGreater(r['serial'], revzonedata['serial']) | |
1497 | ||
1498 | def test_zone_auto_ptr_ipv4_update(self): | |
1d6b70f9 | 1499 | revzone = '0.2.192.in-addr.arpa.' |
a41c038a | 1500 | _, _, revzonedata = self.create_zone(name=revzone) |
6754ef71 | 1501 | name, payload, zone = self.create_zone() |
d708640f | 1502 | rrset = { |
d1587ceb CH |
1503 | 'changetype': 'replace', |
1504 | 'name': name, | |
1505 | 'type': 'A', | |
6754ef71 | 1506 | 'ttl': 3600, |
d1587ceb CH |
1507 | 'records': [ |
1508 | { | |
d1587ceb CH |
1509 | "content": '192.2.0.2', |
1510 | "disabled": False, | |
1511 | "set-ptr": True | |
1512 | } | |
1513 | ] | |
1514 | } | |
d708640f | 1515 | payload = {'rrsets': [rrset]} |
d1587ceb | 1516 | r = self.session.patch( |
46d06a12 | 1517 | self.url("/api/v1/servers/localhost/zones/" + name), |
d1587ceb CH |
1518 | data=json.dumps(payload), |
1519 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1520 | self.assert_success(r) |
a41c038a CH |
1521 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() |
1522 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
541bb91b | 1523 | print(revsets) |
6754ef71 CH |
1524 | self.assertEquals(revsets, [{ |
1525 | u'name': u'2.0.2.192.in-addr.arpa.', | |
d1587ceb | 1526 | u'ttl': 3600, |
d1587ceb | 1527 | u'type': u'PTR', |
6754ef71 CH |
1528 | u'comments': [], |
1529 | u'records': [{ | |
1530 | u'content': name, | |
1531 | u'disabled': False, | |
1532 | }], | |
d1587ceb | 1533 | }]) |
a41c038a CH |
1534 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. |
1535 | self.assertGreater(r['serial'], revzonedata['serial']) | |
d1587ceb | 1536 | |
3fe7c7d6 | 1537 | def test_zone_auto_ptr_ipv6_update(self): |
d1587ceb | 1538 | # 2001:DB8::bb:aa |
1d6b70f9 | 1539 | revzone = '8.b.d.0.1.0.0.2.ip6.arpa.' |
a41c038a | 1540 | _, _, revzonedata = self.create_zone(name=revzone) |
6754ef71 | 1541 | name, payload, zone = self.create_zone() |
d708640f | 1542 | rrset = { |
d1587ceb CH |
1543 | 'changetype': 'replace', |
1544 | 'name': name, | |
1545 | 'type': 'AAAA', | |
6754ef71 | 1546 | 'ttl': 3600, |
d1587ceb CH |
1547 | 'records': [ |
1548 | { | |
d1587ceb CH |
1549 | "content": '2001:DB8::bb:aa', |
1550 | "disabled": False, | |
1551 | "set-ptr": True | |
1552 | } | |
1553 | ] | |
1554 | } | |
d708640f | 1555 | payload = {'rrsets': [rrset]} |
d1587ceb | 1556 | r = self.session.patch( |
46d06a12 | 1557 | self.url("/api/v1/servers/localhost/zones/" + name), |
d1587ceb CH |
1558 | data=json.dumps(payload), |
1559 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1560 | self.assert_success(r) |
a41c038a CH |
1561 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() |
1562 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
541bb91b | 1563 | print(revsets) |
6754ef71 CH |
1564 | self.assertEquals(revsets, [{ |
1565 | u'name': u'a.a.0.0.b.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.', | |
d1587ceb | 1566 | u'ttl': 3600, |
d1587ceb | 1567 | u'type': u'PTR', |
6754ef71 CH |
1568 | u'comments': [], |
1569 | u'records': [{ | |
1570 | u'content': name, | |
1571 | u'disabled': False, | |
1572 | }], | |
d1587ceb | 1573 | }]) |
a41c038a CH |
1574 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. |
1575 | self.assertGreater(r['serial'], revzonedata['serial']) | |
d1587ceb | 1576 | |
c1374bdb | 1577 | def test_search_rr_exact_zone(self): |
b1902fab | 1578 | name = unique_zone_name() |
1d6b70f9 CH |
1579 | self.create_zone(name=name, serial=22, soa_edit_api='') |
1580 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip('.'))) | |
c1374bdb | 1581 | self.assert_success_json(r) |
541bb91b | 1582 | print(r.json()) |
1d6b70f9 CH |
1583 | self.assertEquals(r.json(), [ |
1584 | {u'object_type': u'zone', u'name': name, u'zone_id': name}, | |
1d6b70f9 CH |
1585 | {u'content': u'ns1.example.com.', |
1586 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1587 | u'ttl': 3600, u'type': u'NS', u'name': name}, | |
1588 | {u'content': u'ns2.example.com.', | |
1589 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1590 | u'ttl': 3600, u'type': u'NS', u'name': name}, | |
f2d6dcc0 RG |
1591 | {u'content': u'a.misconfigured.powerdns.server. hostmaster.'+name+' 22 10800 3600 604800 3600', |
1592 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1593 | u'ttl': 3600, u'type': u'SOA', u'name': name}, | |
1d6b70f9 | 1594 | ]) |
b1902fab | 1595 | |
c1374bdb | 1596 | def test_search_rr_substring(self): |
541bb91b CH |
1597 | name = unique_zone_name() |
1598 | search = name[5:-5] | |
b1902fab | 1599 | self.create_zone(name=name) |
541bb91b | 1600 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*%s*" % search)) |
c1374bdb | 1601 | self.assert_success_json(r) |
541bb91b | 1602 | print(r.json()) |
b1902fab | 1603 | # should return zone, SOA, ns1, ns2 |
60a8e825 | 1604 | self.assertEquals(len(r.json()), 4) |
b1902fab | 1605 | |
c1374bdb | 1606 | def test_search_rr_case_insensitive(self): |
541bb91b | 1607 | name = unique_zone_name()+'testsuffix.' |
57cb86d8 | 1608 | self.create_zone(name=name) |
541bb91b | 1609 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*testSUFFIX*")) |
c1374bdb | 1610 | self.assert_success_json(r) |
541bb91b | 1611 | print(r.json()) |
57cb86d8 | 1612 | # should return zone, SOA, ns1, ns2 |
60a8e825 | 1613 | self.assertEquals(len(r.json()), 4) |
57cb86d8 | 1614 | |
7cbc5255 | 1615 | def test_search_after_rectify_with_ent(self): |
541bb91b CH |
1616 | name = unique_zone_name() |
1617 | search = name.split('.')[0] | |
7cbc5255 CH |
1618 | rrset = { |
1619 | "name": 'sub.sub.' + name, | |
1620 | "type": "A", | |
1621 | "ttl": 3600, | |
1622 | "records": [{ | |
1623 | "content": "4.3.2.1", | |
1624 | "disabled": False, | |
1625 | }], | |
1626 | } | |
1627 | self.create_zone(name=name, rrsets=[rrset]) | |
1628 | pdnsutil_rectify(name) | |
541bb91b | 1629 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*%s*" % search)) |
7cbc5255 | 1630 | self.assert_success_json(r) |
541bb91b | 1631 | print(r.json()) |
7cbc5255 CH |
1632 | # should return zone, SOA, ns1, ns2, sub.sub A (but not the ENT) |
1633 | self.assertEquals(len(r.json()), 5) | |
1634 | ||
03b1cc25 CH |
1635 | def test_cname_at_ent_place(self): |
1636 | name, payload, zone = self.create_zone(api_rectify=True) | |
1637 | rrset = { | |
1638 | 'changetype': 'replace', | |
1639 | 'name': 'sub2.sub1.' + name, | |
1640 | 'type': "A", | |
1641 | 'ttl': 3600, | |
1642 | 'records': [{ | |
1643 | 'content': "4.3.2.1", | |
1644 | 'disabled': False, | |
1645 | }], | |
1646 | } | |
1647 | payload = {'rrsets': [rrset]} | |
1648 | r = self.session.patch( | |
1649 | self.url("/api/v1/servers/localhost/zones/" + zone['id']), | |
1650 | data=json.dumps(payload), | |
1651 | headers={'content-type': 'application/json'}) | |
1652 | self.assertEquals(r.status_code, 204) | |
1653 | rrset = { | |
1654 | 'changetype': 'replace', | |
1655 | 'name': 'sub1.' + name, | |
1656 | 'type': "CNAME", | |
1657 | 'ttl': 3600, | |
1658 | 'records': [{ | |
1659 | 'content': "www.example.org.", | |
1660 | 'disabled': False, | |
1661 | }], | |
1662 | } | |
1663 | payload = {'rrsets': [rrset]} | |
1664 | r = self.session.patch( | |
1665 | self.url("/api/v1/servers/localhost/zones/" + zone['id']), | |
1666 | data=json.dumps(payload), | |
1667 | headers={'content-type': 'application/json'}) | |
1668 | self.assertEquals(r.status_code, 204) | |
1669 | ||
986e4858 PL |
1670 | def test_rrset_parameter_post_false(self): |
1671 | name = unique_zone_name() | |
1672 | payload = { | |
1673 | 'name': name, | |
1674 | 'kind': 'Native', | |
1675 | 'nameservers': ['ns1.example.com.', 'ns2.example.com.'] | |
1676 | } | |
1677 | r = self.session.post( | |
1678 | self.url("/api/v1/servers/localhost/zones?rrsets=false"), | |
1679 | data=json.dumps(payload), | |
1680 | headers={'content-type': 'application/json'}) | |
541bb91b | 1681 | print(r.json()) |
986e4858 PL |
1682 | self.assert_success_json(r) |
1683 | self.assertEquals(r.status_code, 201) | |
1684 | self.assertEquals(r.json().get('rrsets'), None) | |
1685 | ||
1686 | def test_rrset_false_parameter(self): | |
1687 | name = unique_zone_name() | |
1688 | self.create_zone(name=name, kind='Native') | |
1689 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/"+name+"?rrsets=false")) | |
1690 | self.assert_success_json(r) | |
541bb91b | 1691 | print(r.json()) |
986e4858 PL |
1692 | self.assertEquals(r.json().get('rrsets'), None) |
1693 | ||
1694 | def test_rrset_true_parameter(self): | |
1695 | name = unique_zone_name() | |
1696 | self.create_zone(name=name, kind='Native') | |
1697 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/"+name+"?rrsets=true")) | |
1698 | self.assert_success_json(r) | |
541bb91b | 1699 | print(r.json()) |
986e4858 PL |
1700 | self.assertEquals(len(r.json().get('rrsets')), 2) |
1701 | ||
1702 | def test_wrong_rrset_parameter(self): | |
1703 | name = unique_zone_name() | |
1704 | self.create_zone(name=name, kind='Native') | |
1705 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/"+name+"?rrsets=foobar")) | |
1706 | self.assertEquals(r.status_code, 422) | |
1707 | self.assertIn("'rrsets' request parameter value 'foobar' is not supported", r.json()['error']) | |
1708 | ||
02945d9a | 1709 | |
406497f5 CH |
1710 | @unittest.skipIf(not is_auth(), "Not applicable") |
1711 | class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): | |
1712 | ||
1713 | def setUp(self): | |
1714 | super(AuthRootZone, self).setUp() | |
1715 | # zone name is not unique, so delete the zone before each individual test. | |
46d06a12 | 1716 | self.session.delete(self.url("/api/v1/servers/localhost/zones/=2E")) |
406497f5 CH |
1717 | |
1718 | def test_create_zone(self): | |
6754ef71 | 1719 | name, payload, data = self.create_zone(name='.', serial=22, soa_edit_api='') |
406497f5 CH |
1720 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'): |
1721 | self.assertIn(k, data) | |
1722 | if k in payload: | |
1723 | self.assertEquals(data[k], payload[k]) | |
406497f5 | 1724 | # validate generated SOA |
6754ef71 | 1725 | rec = get_first_rec(data, '.', 'SOA') |
406497f5 | 1726 | self.assertEquals( |
6754ef71 | 1727 | rec['content'], |
1d6b70f9 | 1728 | "a.misconfigured.powerdns.server. hostmaster. " + str(payload['serial']) + |
406497f5 CH |
1729 | " 10800 3600 604800 3600" |
1730 | ) | |
1731 | # Regression test: verify zone list works | |
46d06a12 | 1732 | zonelist = self.session.get(self.url("/api/v1/servers/localhost/zones")).json() |
541bb91b | 1733 | print("zonelist:", zonelist) |
406497f5 CH |
1734 | self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) |
1735 | # Also test that fetching the zone works. | |
541bb91b | 1736 | print("id:", data['id']) |
406497f5 | 1737 | self.assertEquals(data['id'], '=2E') |
46d06a12 | 1738 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])).json() |
541bb91b | 1739 | print("zone (fetched):", data) |
406497f5 CH |
1740 | for k in ('name', 'kind'): |
1741 | self.assertIn(k, data) | |
1742 | self.assertEquals(data[k], payload[k]) | |
6754ef71 | 1743 | self.assertEqual(data['rrsets'][0]['name'], '.') |
406497f5 CH |
1744 | |
1745 | def test_update_zone(self): | |
6754ef71 | 1746 | name, payload, zone = self.create_zone(name='.') |
406497f5 CH |
1747 | zone_id = '=2E' |
1748 | # update, set as Master and enable SOA-EDIT-API | |
1749 | payload = { | |
1750 | 'kind': 'Master', | |
1751 | 'masters': ['192.0.2.1', '192.0.2.2'], | |
1752 | 'soa_edit_api': 'EPOCH', | |
1753 | 'soa_edit': 'EPOCH' | |
1754 | } | |
1755 | r = self.session.put( | |
46d06a12 | 1756 | self.url("/api/v1/servers/localhost/zones/" + zone_id), |
406497f5 CH |
1757 | data=json.dumps(payload), |
1758 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1759 | self.assert_success(r) |
1760 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)).json() | |
406497f5 CH |
1761 | for k in payload.keys(): |
1762 | self.assertIn(k, data) | |
1763 | self.assertEquals(data[k], payload[k]) | |
1764 | # update, back to Native and empty(off) | |
1765 | payload = { | |
1766 | 'kind': 'Native', | |
1767 | 'soa_edit_api': '', | |
1768 | 'soa_edit': '' | |
1769 | } | |
1770 | r = self.session.put( | |
46d06a12 | 1771 | self.url("/api/v1/servers/localhost/zones/" + zone_id), |
406497f5 CH |
1772 | data=json.dumps(payload), |
1773 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1774 | self.assert_success(r) |
1775 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)).json() | |
406497f5 CH |
1776 | for k in payload.keys(): |
1777 | self.assertIn(k, data) | |
1778 | self.assertEquals(data[k], payload[k]) | |
1779 | ||
1780 | ||
c1374bdb | 1781 | @unittest.skipIf(not is_recursor(), "Not applicable") |
02945d9a CH |
1782 | class RecursorZones(ApiTestCase): |
1783 | ||
37bc3d01 CH |
1784 | def create_zone(self, name=None, kind=None, rd=False, servers=None): |
1785 | if name is None: | |
1786 | name = unique_zone_name() | |
1787 | if servers is None: | |
1788 | servers = [] | |
02945d9a | 1789 | payload = { |
37bc3d01 CH |
1790 | 'name': name, |
1791 | 'kind': kind, | |
1792 | 'servers': servers, | |
1793 | 'recursion_desired': rd | |
02945d9a CH |
1794 | } |
1795 | r = self.session.post( | |
46d06a12 | 1796 | self.url("/api/v1/servers/localhost/zones"), |
02945d9a CH |
1797 | data=json.dumps(payload), |
1798 | headers={'content-type': 'application/json'}) | |
c1374bdb CH |
1799 | self.assert_success_json(r) |
1800 | return payload, r.json() | |
37bc3d01 | 1801 | |
c1374bdb | 1802 | def test_create_auth_zone(self): |
37bc3d01 | 1803 | payload, data = self.create_zone(kind='Native') |
02945d9a CH |
1804 | for k in payload.keys(): |
1805 | self.assertEquals(data[k], payload[k]) | |
1806 | ||
1d6b70f9 | 1807 | def test_create_zone_no_name(self): |
1d6b70f9 CH |
1808 | payload = { |
1809 | 'name': '', | |
1810 | 'kind': 'Native', | |
1811 | 'servers': ['8.8.8.8'], | |
1812 | 'recursion_desired': False, | |
1813 | } | |
541bb91b | 1814 | print(payload) |
1d6b70f9 CH |
1815 | r = self.session.post( |
1816 | self.url("/api/v1/servers/localhost/zones"), | |
1817 | data=json.dumps(payload), | |
1818 | headers={'content-type': 'application/json'}) | |
1819 | self.assertEquals(r.status_code, 422) | |
1820 | self.assertIn('is not canonical', r.json()['error']) | |
1821 | ||
c1374bdb | 1822 | def test_create_forwarded_zone(self): |
37bc3d01 | 1823 | payload, data = self.create_zone(kind='Forwarded', rd=False, servers=['8.8.8.8']) |
02945d9a CH |
1824 | # return values are normalized |
1825 | payload['servers'][0] += ':53' | |
02945d9a CH |
1826 | for k in payload.keys(): |
1827 | self.assertEquals(data[k], payload[k]) | |
1828 | ||
c1374bdb | 1829 | def test_create_forwarded_rd_zone(self): |
1d6b70f9 | 1830 | payload, data = self.create_zone(name='google.com.', kind='Forwarded', rd=True, servers=['8.8.8.8']) |
02945d9a CH |
1831 | # return values are normalized |
1832 | payload['servers'][0] += ':53' | |
02945d9a CH |
1833 | for k in payload.keys(): |
1834 | self.assertEquals(data[k], payload[k]) | |
1835 | ||
c1374bdb | 1836 | def test_create_auth_zone_with_symbols(self): |
37bc3d01 | 1837 | payload, data = self.create_zone(name='foo/bar.'+unique_zone_name(), kind='Native') |
1dbe38ba | 1838 | expected_id = (payload['name'].replace('/', '=2F')) |
02945d9a CH |
1839 | for k in payload.keys(): |
1840 | self.assertEquals(data[k], payload[k]) | |
1841 | self.assertEquals(data['id'], expected_id) | |
e2367534 | 1842 | |
c1374bdb | 1843 | def test_rename_auth_zone(self): |
37bc3d01 | 1844 | payload, data = self.create_zone(kind='Native') |
1d6b70f9 | 1845 | name = payload['name'] |
e2367534 CH |
1846 | # now rename it |
1847 | payload = { | |
1848 | 'name': 'renamed-'+name, | |
1849 | 'kind': 'Native', | |
1850 | 'recursion_desired': False | |
1851 | } | |
1852 | r = self.session.put( | |
46d06a12 | 1853 | self.url("/api/v1/servers/localhost/zones/" + name), |
e2367534 CH |
1854 | data=json.dumps(payload), |
1855 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1856 | self.assert_success(r) |
1857 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + payload['name'])).json() | |
e2367534 CH |
1858 | for k in payload.keys(): |
1859 | self.assertEquals(data[k], payload[k]) | |
37bc3d01 | 1860 | |
37663c3b CH |
1861 | def test_zone_delete(self): |
1862 | payload, zone = self.create_zone(kind='Native') | |
1863 | name = payload['name'] | |
46d06a12 | 1864 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) |
37663c3b CH |
1865 | self.assertEquals(r.status_code, 204) |
1866 | self.assertNotIn('Content-Type', r.headers) | |
1867 | ||
c1374bdb | 1868 | def test_search_rr_exact_zone(self): |
1d6b70f9 | 1869 | name = unique_zone_name() |
37bc3d01 | 1870 | self.create_zone(name=name, kind='Native') |
46d06a12 | 1871 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name)) |
c1374bdb | 1872 | self.assert_success_json(r) |
541bb91b | 1873 | print(r.json()) |
37bc3d01 CH |
1874 | self.assertEquals(r.json(), [{u'type': u'zone', u'name': name, u'zone_id': name}]) |
1875 | ||
c1374bdb | 1876 | def test_search_rr_substring(self): |
1d6b70f9 | 1877 | name = 'search-rr-zone.name.' |
37bc3d01 | 1878 | self.create_zone(name=name, kind='Native') |
46d06a12 | 1879 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=rr-zone")) |
c1374bdb | 1880 | self.assert_success_json(r) |
541bb91b | 1881 | print(r.json()) |
37bc3d01 CH |
1882 | # should return zone, SOA |
1883 | self.assertEquals(len(r.json()), 2) | |
ccfabd0d | 1884 | |
ccfabd0d CH |
1885 | @unittest.skipIf(not is_auth(), "Not applicable") |
1886 | class AuthZoneKeys(ApiTestCase, AuthZonesHelperMixin): | |
1887 | ||
1888 | def test_get_keys(self): | |
1889 | r = self.session.get( | |
1890 | self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys")) | |
1891 | self.assert_success_json(r) | |
1892 | keys = r.json() | |
1893 | self.assertGreater(len(keys), 0) | |
1894 | ||
1895 | key0 = deepcopy(keys[0]) | |
1896 | del key0['dnskey'] | |
b6bd795c | 1897 | del key0['ds'] |
ccfabd0d | 1898 | expected = { |
5d9c6182 PL |
1899 | u'algorithm': u'ECDSAP256SHA256', |
1900 | u'bits': 256, | |
ccfabd0d CH |
1901 | u'active': True, |
1902 | u'type': u'Cryptokey', | |
b6bd795c PL |
1903 | u'keytype': u'csk', |
1904 | u'flags': 257, | |
ccfabd0d CH |
1905 | u'id': 1} |
1906 | self.assertEquals(key0, expected) | |
1907 | ||
1908 | keydata = keys[0]['dnskey'].split() | |
1909 | self.assertEqual(len(keydata), 4) |