From b139d266c9a2ec1c236c4962bdb5b70029039b80 Mon Sep 17 00:00:00 2001 From: Aquifoliales <103569748+Aquifoliales@users.noreply.github.com> Date: Wed, 4 May 2022 11:01:50 +0200 Subject: [PATCH 1/4] Support for TLSA record, https://www.rfc-editor.org/rfc/rfc6698.txt --- docs/records.md | 1 + octodns/record/__init__.py | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/docs/records.md b/docs/records.md index 182a109..30d689a 100644 --- a/docs/records.md +++ b/docs/records.md @@ -18,6 +18,7 @@ OctoDNS supports the following record types: * `SPF` * `SRV` * `SSHFP` +* `TLSA` * `TXT` * `URLFWD` diff --git a/octodns/record/__init__.py b/octodns/record/__init__.py index 6fe3f90..ce73752 100644 --- a/octodns/record/__init__.py +++ b/octodns/record/__init__.py @@ -1517,6 +1517,71 @@ class SrvRecord(ValuesMixin, Record): Record.register_type(SrvRecord) +class TlsaValue(EqualityTupleMixin): + + @classmethod + def validate(cls, data, _type): + if not isinstance(data, (list, tuple)): + data = (data,) + reasons = [] + for value in data: + try: + certificate_usage = int(value.get('certificate_usage', 0)) + if certificate_usage < 0 or certificate_usage > 3: + reasons.append(f'invalid certificate_usage "{certificate_usage}"') + except ValueError: + reasons.append(f'invalid certificate_usage "{value["certificate_usage"]}"') + + try: + selector = int(value.get('selector', 0)) + if selector < 0 or selector > 1: + reasons.append(f'invalid selector "{selector}"') + except ValueError: + reasons.append(f'invalid selector "{value["selector"]}"') + + try: + matching_type = int(value.get('matching_type', 0)) + if matching_type < 0 or matching_type > 2: + reasons.append(f'invalid matching_type "{matching_type}"') + except ValueError: + reasons.append(f'invalid matching_type "{value["matching_type"]}"') + + if 'certificate_association_data' not in value: + reasons.append('missing certificate_association_data') + return reasons + + @classmethod + def process(cls, values): + return [TlsaValue(v) for v in values] + + def __init__(self, value): + self.certificate_usage = int(value.get('certificate_usage', 0)) + self.selector = int(value.get('selector', 0)) + self.matching_type = int(value.get('matching_type', 0)) + self.certificate_association_data = value['certificate_association_data'] + + @property + def data(self): + return { + 'certificate_usage': self.certificate_usage, + 'selector': self.selector, + 'matching_type': self.matching_type, + 'certificate_association_data': self.certificate_association_data, + } + + def _equality_tuple(self): + return (self.certificate_usage, self.selector, self.matching_type, self.certificate_association_data) + + def __repr__(self): + return f'{self.certificate_usage} {self.selector} {self.matching_type}"{self.certificate_association_data}"' + + +class TlsaRecord(ValuesMixin, Record): + _type = 'TLSA' + _value_type = TlsaValue + +Record.register_type(TlsaRecord) + class _TxtValue(_ChunkedValue): pass From 084d537c943fc282411d08cdc1f83b9d4aadd755 Mon Sep 17 00:00:00 2001 From: Aquifoliales <103569748+Aquifoliales@users.noreply.github.com> Date: Tue, 10 May 2022 09:50:57 +0200 Subject: [PATCH 2/4] Fixed testing for TLSA record type. --- octodns/record/__init__.py | 28 +++- tests/test_octodns_record.py | 245 ++++++++++++++++++++++++++++++++++- 2 files changed, 263 insertions(+), 10 deletions(-) diff --git a/octodns/record/__init__.py b/octodns/record/__init__.py index ce73752..ddb8b92 100644 --- a/octodns/record/__init__.py +++ b/octodns/record/__init__.py @@ -1528,9 +1528,11 @@ class TlsaValue(EqualityTupleMixin): try: certificate_usage = int(value.get('certificate_usage', 0)) if certificate_usage < 0 or certificate_usage > 3: - reasons.append(f'invalid certificate_usage "{certificate_usage}"') + reasons.append(f'invalid certificate_usage ' + f'"{certificate_usage}"') except ValueError: - reasons.append(f'invalid certificate_usage "{value["certificate_usage"]}"') + reasons.append(f'invalid certificate_usage ' + f'"{value["certificate_usage"]}"') try: selector = int(value.get('selector', 0)) @@ -1544,8 +1546,15 @@ class TlsaValue(EqualityTupleMixin): if matching_type < 0 or matching_type > 2: reasons.append(f'invalid matching_type "{matching_type}"') except ValueError: - reasons.append(f'invalid matching_type "{value["matching_type"]}"') - + reasons.append(f'invalid matching_type ' + f'"{value["matching_type"]}"') + + if 'certificate_usage' not in value: + reasons.append('missing certificate_usage') + if 'selector' not in value: + reasons.append('missing selector') + if 'matching_type' not in value: + reasons.append('missing matching_type') if 'certificate_association_data' not in value: reasons.append('missing certificate_association_data') return reasons @@ -1558,7 +1567,8 @@ class TlsaValue(EqualityTupleMixin): self.certificate_usage = int(value.get('certificate_usage', 0)) self.selector = int(value.get('selector', 0)) self.matching_type = int(value.get('matching_type', 0)) - self.certificate_association_data = value['certificate_association_data'] + self.certificate_association_data = \ + value['certificate_association_data'] @property def data(self): @@ -1570,18 +1580,22 @@ class TlsaValue(EqualityTupleMixin): } def _equality_tuple(self): - return (self.certificate_usage, self.selector, self.matching_type, self.certificate_association_data) + return (self.certificate_usage, self.selector, self.matching_type, + self.certificate_association_data) def __repr__(self): - return f'{self.certificate_usage} {self.selector} {self.matching_type}"{self.certificate_association_data}"' + return f"'{self.certificate_usage} {self.selector} '" \ + f"'{self.matching_type} {self.certificate_association_data}'" class TlsaRecord(ValuesMixin, Record): _type = 'TLSA' _value_type = TlsaValue + Record.register_type(TlsaRecord) + class _TxtValue(_ChunkedValue): pass diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index fd3f70f..dbd2666 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -11,9 +11,9 @@ from octodns.record import ARecord, AaaaRecord, AliasRecord, CaaRecord, \ CaaValue, CnameRecord, DnameRecord, Create, Delete, GeoValue, LocRecord, \ LocValue, MxRecord, MxValue, NaptrRecord, NaptrValue, NsRecord, \ PtrRecord, Record, RecordException, SshfpRecord, SshfpValue, SpfRecord, \ - SrvRecord, SrvValue, TxtRecord, Update, UrlfwdRecord, UrlfwdValue, \ - ValidationError, _Dynamic, _DynamicPool, _DynamicRule, _NsValue, \ - ValuesMixin + SrvRecord, SrvValue, TlsaRecord, TxtRecord, Update, UrlfwdRecord, \ + UrlfwdValue, ValidationError, _Dynamic, _DynamicPool, _DynamicRule, \ + _NsValue, ValuesMixin from octodns.zone import Zone from helpers import DynamicProvider, GeoProvider, SimpleProvider @@ -907,6 +907,80 @@ class TestRecord(TestCase): # __repr__ doesn't blow up a.__repr__() + def test_tlsa(self): + a_values = [{ + 'certificate_usage': 1, + 'selector': 1, + 'matching_type': 1, + 'certificate_association_data': 'ABABABABABABABABAB', + }, { + 'certificate_usage': 2, + 'selector': 0, + 'matching_type': 2, + 'certificate_association_data': 'ABABABABABABABABAC', + }] + a_data = {'ttl': 30, 'values': a_values} + a = TlsaRecord(self.zone, 'a', a_data) + self.assertEqual('a.unit.tests.', a.fqdn) + self.assertEqual('a', a.name) + self.assertEqual(30, a.ttl) + self.assertEqual(a_values[0]['certificate_usage'], a.values[0].certificate_usage) + self.assertEqual(a_values[0]['selector'], a.values[0].selector) + self.assertEqual(a_values[0]['matching_type'], a.values[0].matching_type) + self.assertEqual(a_values[0]['certificate_association_data'], a.values[0].certificate_association_data) + + self.assertEqual(a_values[1]['certificate_usage'], a.values[1].certificate_usage) + self.assertEqual(a_values[1]['selector'], a.values[1].selector) + self.assertEqual(a_values[1]['matching_type'], a.values[1].matching_type) + self.assertEqual(a_values[1]['certificate_association_data'], a.values[1].certificate_association_data) + self.assertEqual(a_data, a.data) + + b_value = { + 'certificate_usage': 0, + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAAAA', + } + b_data = {'ttl': 30, 'value': b_value} + b = TlsaRecord(self.zone, 'b', b_data) + self.assertEqual(b_value['certificate_usage'], b.values[0].certificate_usage) + self.assertEqual(b_value['selector'], b.values[0].selector) + self.assertEqual(b_value['matching_type'], b.values[0].matching_type) + self.assertEqual(b_value['certificate_association_data'], b.values[0].certificate_association_data) + self.assertEqual(b_data, b.data) + + target = SimpleProvider() + # No changes with self + self.assertFalse(a.changes(a, target)) + # Diff in certificate_usage causes change + other = TlsaRecord(self.zone, 'a', {'ttl': 30, 'values': a_values}) + other.values[0].certificate_usage = 0 + change = a.changes(other, target) + self.assertEqual(change.existing, a) + self.assertEqual(change.new, other) + # Diff in selector causes change + other = TlsaRecord(self.zone, 'a', {'ttl': 30, 'values': a_values}) + other.values[0].selector = 0 + change = a.changes(other, target) + self.assertEqual(change.existing, a) + self.assertEqual(change.new, other) + # Diff in matching_type causes change + other = TlsaRecord(self.zone, 'a', {'ttl': 30, 'values': a_values}) + other.values[0].matching_type = 0 + change = a.changes(other, target) + self.assertEqual(change.existing, a) + self.assertEqual(change.new, other) + # Diff in certificate_association_data causes change + other = TlsaRecord(self.zone, 'a', {'ttl': 30, 'values': a_values}) + other.values[0].certificate_association_data = 'AAAAAAAAAAAAA' + change = a.changes(other, target) + self.assertEqual(change.existing, a) + self.assertEqual(change.new, other) + + # __repr__ doesn't blow up + a.__repr__() + + def test_txt(self): a_values = ['a one', 'a two'] b_value = 'b other' @@ -3187,6 +3261,171 @@ class TestRecordValidation(TestCase): self.assertEqual(['Invalid SRV target "100 foo.bar.com." is not a ' 'valid FQDN.'], ctx.exception.reasons) + def test_TLSA(self): + # doesn't blow up + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 0, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + + # missing certificate_association_data + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 0, + 'matching_type' : 0 + } + }) + self.assertEqual(['missing certificate_association_data'], + ctx.exception.reasons) + + # missing certificate_usage + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'selector' : 0, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual(['missing certificate_usage'], + ctx.exception.reasons) + + # False certificate_usage + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 4, + 'selector' : 0, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid certificate_usage ' + '"{value["certificate_usage"]}"', + ctx.exception.reasons) + + # Invalid certificate_usage + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 'XYZ', + 'selector' : 0, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid certificate_usage ' + '"{value["certificate_usage"]}"', + ctx.exception.reasons) + + # missing selector + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual(['missing selector'], + ctx.exception.reasons) + + # False selector + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 4, + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid selector ' + '"{value["selector"]}"', + ctx.exception.reasons) + + # Invalid selector + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 'XYZ', + 'matching_type' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid selector ' + '"{value["selector"]}"', + ctx.exception.reasons) + + # missing matching_type + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 0, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual(['missing matching_type'], + ctx.exception.reasons) + + # False matching_type + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 1, + 'matching_type' : 3, + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid matching_type ' + '"{value["matching_type"]}"', + ctx.exception.reasons) + + # Invalid matching_type + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'value' : { + 'certificate_usage' : 0, + 'selector' : 1, + 'matching_type' : 'XYZ', + 'certificate_association_data' : 'AAAAAAAAAAAAA' + } + }) + self.assertEqual('invalid matching_type ' + '"{value["matching_type"]}"', + ctx.exception.reasons) + def test_TXT(self): # doesn't blow up (name & zone here don't make any sense, but not # important) From 3cdefc505844e3253a7e2b179b7a41306ca557a1 Mon Sep 17 00:00:00 2001 From: Aquifoliales <103569748+Aquifoliales@users.noreply.github.com> Date: Tue, 10 May 2022 14:58:35 +0200 Subject: [PATCH 3/4] Adaption for Linting --- tests/test_octodns_record.py | 187 ++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 89 deletions(-) diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index dbd2666..2649fb9 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -924,17 +924,24 @@ class TestRecord(TestCase): self.assertEqual('a.unit.tests.', a.fqdn) self.assertEqual('a', a.name) self.assertEqual(30, a.ttl) - self.assertEqual(a_values[0]['certificate_usage'], a.values[0].certificate_usage) + self.assertEqual(a_values[0]['certificate_usage'], + a.values[0].certificate_usage) self.assertEqual(a_values[0]['selector'], a.values[0].selector) - self.assertEqual(a_values[0]['matching_type'], a.values[0].matching_type) - self.assertEqual(a_values[0]['certificate_association_data'], a.values[0].certificate_association_data) - - self.assertEqual(a_values[1]['certificate_usage'], a.values[1].certificate_usage) - self.assertEqual(a_values[1]['selector'], a.values[1].selector) - self.assertEqual(a_values[1]['matching_type'], a.values[1].matching_type) - self.assertEqual(a_values[1]['certificate_association_data'], a.values[1].certificate_association_data) + self.assertEqual(a_values[0]['matching_type'], + a.values[0].matching_type) + self.assertEqual(a_values[0]['certificate_association_data'], + a.values[0].certificate_association_data) + + self.assertEqual(a_values[1]['certificate_usage'], + a.values[1].certificate_usage) + self.assertEqual(a_values[1]['selector'], + a.values[1].selector) + self.assertEqual(a_values[1]['matching_type'], + a.values[1].matching_type) + self.assertEqual(a_values[1]['certificate_association_data'], + a.values[1].certificate_association_data) self.assertEqual(a_data, a.data) - + b_value = { 'certificate_usage': 0, 'selector': 0, @@ -943,10 +950,13 @@ class TestRecord(TestCase): } b_data = {'ttl': 30, 'value': b_value} b = TlsaRecord(self.zone, 'b', b_data) - self.assertEqual(b_value['certificate_usage'], b.values[0].certificate_usage) + self.assertEqual(b_value['certificate_usage'], + b.values[0].certificate_usage) self.assertEqual(b_value['selector'], b.values[0].selector) - self.assertEqual(b_value['matching_type'], b.values[0].matching_type) - self.assertEqual(b_value['certificate_association_data'], b.values[0].certificate_association_data) + self.assertEqual(b_value['matching_type'], + b.values[0].matching_type) + self.assertEqual(b_value['certificate_association_data'], + b.values[0].certificate_association_data) self.assertEqual(b_data, b.data) target = SimpleProvider() @@ -976,11 +986,10 @@ class TestRecord(TestCase): change = a.changes(other, target) self.assertEqual(change.existing, a) self.assertEqual(change.new, other) - + # __repr__ doesn't blow up a.__repr__() - def test_txt(self): a_values = ['a one', 'a two'] b_value = 'b other' @@ -3266,11 +3275,11 @@ class TestRecordValidation(TestCase): Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 0, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) @@ -3279,152 +3288,152 @@ class TestRecordValidation(TestCase): Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 0, - 'matching_type' : 0 + 'value': { + 'certificate_usage': 0, + 'selector': 0, + 'matching_type': 0 } }) self.assertEqual(['missing certificate_association_data'], - ctx.exception.reasons) + ctx.exception.reasons) - # missing certificate_usage + # missing certificate_usage with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'selector' : 0, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual(['missing certificate_usage'], - ctx.exception.reasons) + ctx.exception.reasons) # False certificate_usage with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 4, - 'selector' : 0, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 4, + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid certificate_usage ' - '"{value["certificate_usage"]}"', - ctx.exception.reasons) + '"{value["certificate_usage"]}"', + ctx.exception.reasons) # Invalid certificate_usage with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 'XYZ', - 'selector' : 0, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 'XYZ', + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid certificate_usage ' - '"{value["certificate_usage"]}"', - ctx.exception.reasons) + '"{value["certificate_usage"]}"', + ctx.exception.reasons) - # missing selector + # missing selector with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual(['missing selector'], - ctx.exception.reasons) + ctx.exception.reasons) - # False selector + # False selector with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 4, - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 4, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid selector ' - '"{value["selector"]}"', - ctx.exception.reasons) + '"{value["selector"]}"', + ctx.exception.reasons) - # Invalid selector + # Invalid selector with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 'XYZ', - 'matching_type' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 'XYZ', + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid selector ' - '"{value["selector"]}"', - ctx.exception.reasons) - - # missing matching_type + '"{value["selector"]}"', + ctx.exception.reasons) + + # missing matching_type with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 0, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual(['missing matching_type'], - ctx.exception.reasons) + ctx.exception.reasons) - # False matching_type + # False matching_type with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 1, - 'matching_type' : 3, - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 1, + 'matching_type': 3, + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid matching_type ' - '"{value["matching_type"]}"', - ctx.exception.reasons) + '"{value["matching_type"]}"', + ctx.exception.reasons) - # Invalid matching_type + # Invalid matching_type with self.assertRaises(ValidationError) as ctx: Record.new(self.zone, '', { 'type': 'TLSA', 'ttl': 600, - 'value' : { - 'certificate_usage' : 0, - 'selector' : 1, - 'matching_type' : 'XYZ', - 'certificate_association_data' : 'AAAAAAAAAAAAA' + 'value': { + 'certificate_usage': 0, + 'selector': 1, + 'matching_type': 'XYZ', + 'certificate_association_data': 'AAAAAAAAAAAAA' } }) self.assertEqual('invalid matching_type ' - '"{value["matching_type"]}"', - ctx.exception.reasons) + '"{value["matching_type"]}"', + ctx.exception.reasons) def test_TXT(self): # doesn't blow up (name & zone here don't make any sense, but not From 2401a7318c95ae794ddc2ec58b011461d8d386d6 Mon Sep 17 00:00:00 2001 From: Aquifoliales <103569748+Aquifoliales@users.noreply.github.com> Date: Tue, 10 May 2022 15:56:33 +0200 Subject: [PATCH 4/4] Fixed testing, TLSA record ready. --- tests/test_octodns_record.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index 2649fb9..a406d96 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -3282,6 +3282,25 @@ class TestRecordValidation(TestCase): 'certificate_association_data': 'AAAAAAAAAAAAA' } }) + # Multi value, second missing certificate usage + with self.assertRaises(ValidationError) as ctx: + Record.new(self.zone, '', { + 'type': 'TLSA', + 'ttl': 600, + 'values': [{ + 'certificate_usage': 0, + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' + }, { + 'selector': 0, + 'matching_type': 0, + 'certificate_association_data': 'AAAAAAAAAAAAA' + } + ] + }) + self.assertEqual(['missing certificate_usage'], + ctx.exception.reasons) # missing certificate_association_data with self.assertRaises(ValidationError) as ctx: