From f93424f2f20643aab6c034303c0ee3c2efa266f0 Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Mon, 8 Jul 2019 16:30:45 +0200 Subject: [PATCH] Fix #242 - Update dates in new changed: lines when override is not used. (#244) Backport of d68ce2a1535051c4a433c6b75100f3bbe40a974d from master. * Add field type for changed lines. * Add RPSLObject support for overwriting new changed lines. * Update dates in new changed: lines when override is not used. --- irrd/integration_tests/run.py | 2 +- irrd/rpsl/fields.py | 23 ++++++++ irrd/rpsl/parser.py | 40 +++++++++++++ irrd/rpsl/rpsl_objects.py | 36 ++++++------ irrd/rpsl/tests/test_fields.py | 17 +++++- irrd/rpsl/tests/test_rpsl_objects.py | 35 ++++++++++++ irrd/scripts/tests/test_rpsl_read.py | 6 +- irrd/updates/parser.py | 20 ++++++- irrd/updates/tests/test_handler.py | 84 ++++++++++++++++------------ irrd/updates/tests/test_parser.py | 12 ++-- irrd/utils/rpsl_samples.py | 82 +++++++++++++-------------- 11 files changed, 250 insertions(+), 107 deletions(-) diff --git a/irrd/integration_tests/run.py b/irrd/integration_tests/run.py index 96b54fd5e..751e7e9e6 100644 --- a/irrd/integration_tests/run.py +++ b/irrd/integration_tests/run.py @@ -33,7 +33,7 @@ admin-c: PERSON-TEST notify: notify@example.com mnt-by: TEST-MNT -changed: 2017-05-19T12:22:08Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ diff --git a/irrd/rpsl/fields.py b/irrd/rpsl/fields.py index aa422c64e..2f46c3bd3 100644 --- a/irrd/rpsl/fields.py +++ b/irrd/rpsl/fields.py @@ -1,3 +1,4 @@ +import datetime import re from typing import List, Type, Optional @@ -328,6 +329,28 @@ def parse(self, value: str, messages: RPSLParserMessages, strict_validation=True return RPSLFieldParseResult(value) +class RPSLChangedField(RPSLTextField): + """Field for an changed line. Only performs basic validation for email.""" + def parse(self, value: str, messages: RPSLParserMessages, strict_validation=True) -> Optional[RPSLFieldParseResult]: + date: Optional[str] + try: + email, date = value.split(' ') + except ValueError: + email = value + date = None + + if not re_email.match(email): + messages.error(f'Invalid e-mail address: {email}') + return None + if date: + try: + datetime.datetime.strptime(date, '%Y%m%d') + except ValueError as ve: + messages.error(f'Invalid changed date: {date}: {ve}') + return None + return RPSLFieldParseResult(value) + + class RPSLDNSNameField(RPSLTextField): """Field for a DNS name, as used in e.g. inet-rtr names.""" keep_case = False diff --git a/irrd/rpsl/parser.py b/irrd/rpsl/parser.py index e43a42231..75d0e1f12 100644 --- a/irrd/rpsl/parser.py +++ b/irrd/rpsl/parser.py @@ -1,3 +1,4 @@ +import datetime import json import re from collections import OrderedDict, Counter @@ -397,6 +398,45 @@ def _update_attribute_value(self, attribute, new_values): self._object_data.insert(insert_idx, (attribute, new_value, [])) insert_idx += 1 + def overwrite_date_new_changed_attributes(self, existing_obj=None) -> None: + """ + Overwrite the date in any newly added changed: attributes per #242. + Which changed: lines are new is determined by comparing to existing_obj, + which should be another RPSLObject, or None if all changed: lines + should be considered new. + """ + parsed_values_to_overwrite = set(self.parsed_data['changed']) + if existing_obj: + parsed_values_to_overwrite -= set(existing_obj.parsed_data['changed']) + + # As the value is already validated by RPSLChangedField, + # we can safely make assumptions on the format. + new_object_data = [] + removed_values_with_comment: List[Tuple[int, str]] = [] + for idx, (attr_name, attr_value, continuation_chars) in enumerate(self._object_data): + if attr_name == 'changed': + attr_value_clean = attr_value.split('#')[0].strip() + if attr_value_clean in parsed_values_to_overwrite: + removed_values_with_comment.append((idx, attr_value)) + continue + new_object_data.append((attr_name, attr_value, continuation_chars)) + self._object_data = new_object_data + + current_date = datetime.datetime.now().strftime('%Y%m%d') + for idx, value in removed_values_with_comment: + try: + content, comment = map(str.strip, value.split('#')) + except ValueError: + content = value.strip() + comment = '' + email = content.split(' ')[0] # Ignore existing date + if comment: + new_value = f'{email} {current_date} # {comment}' + else: + new_value = f'{email} {current_date}' + self._object_data.insert(idx, ('changed', new_value, [])) + self.messages.info(f'Set date in changed line "{value}" to today.') + def __repr__(self): source = self.parsed_data.get('source', '') return f'{self.rpsl_object_class}/{self.pk()}/{source}' diff --git a/irrd/rpsl/rpsl_objects.py b/irrd/rpsl/rpsl_objects.py index 39f279ab5..51345513d 100644 --- a/irrd/rpsl/rpsl_objects.py +++ b/irrd/rpsl/rpsl_objects.py @@ -8,7 +8,7 @@ from .fields import (RPSLTextField, RPSLIPv4PrefixField, RPSLIPv4PrefixesField, RPSLIPv6PrefixField, RPSLIPv6PrefixesField, RPSLIPv4AddressRangeField, RPSLASNumberField, RPSLASBlockField, RPSLSetNameField, RPSLEmailField, RPSLDNSNameField, RPSLGenericNameField, RPSLReferenceField, - RPSLReferenceListField, RPSLAuthField, RPSLRouteSetMembersField) + RPSLReferenceListField, RPSLAuthField, RPSLRouteSetMembersField, RPSLChangedField) from .parser import RPSLObject, UnknownRPSLObjectClassException @@ -31,7 +31,7 @@ class RPSLAsBlock(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -47,7 +47,7 @@ class RPSLAsSet(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -71,7 +71,7 @@ class RPSLAutNum(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, optional=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -90,7 +90,7 @@ class RPSLDomain(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, optional=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -106,7 +106,7 @@ class RPSLFilterSet(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -129,7 +129,7 @@ class RPSLInetRtr(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -147,7 +147,7 @@ class RPSLInet6Num(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -165,7 +165,7 @@ class RPSLInetnum(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -182,7 +182,7 @@ class RPSLKeyCert(RPSLObject): ('tech-c', RPSLReferenceField(lookup_key=True, optional=True, multiple=True, referring=['role', 'person'])), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -264,7 +264,7 @@ class RPSLMntner(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -342,7 +342,7 @@ class RPSLPeeringSet(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -358,7 +358,7 @@ class RPSLPerson(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -377,7 +377,7 @@ class RPSLRole(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -401,7 +401,7 @@ class RPSLRoute(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -418,7 +418,7 @@ class RPSLRouteSet(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -442,7 +442,7 @@ class RPSLRoute6(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) @@ -459,7 +459,7 @@ class RPSLRtrSet(RPSLObject): ('remarks', RPSLTextField(optional=True, multiple=True)), ('notify', RPSLEmailField(optional=True, multiple=True)), ('mnt-by', RPSLReferenceListField(lookup_key=True, multiple=True, referring=['mntner'])), - ('changed', RPSLTextField(multiple=True)), + ('changed', RPSLChangedField(multiple=True)), ('source', RPSLGenericNameField()), ]) diff --git a/irrd/rpsl/tests/test_fields.py b/irrd/rpsl/tests/test_fields.py index 55241db2f..fd035e699 100644 --- a/irrd/rpsl/tests/test_fields.py +++ b/irrd/rpsl/tests/test_fields.py @@ -4,7 +4,7 @@ from ..fields import (RPSLIPv4PrefixField, RPSLIPv4PrefixesField, RPSLIPv6PrefixField, RPSLIPv6PrefixesField, RPSLIPv4AddressRangeField, RPSLASNumberField, RPSLASBlockField, RPSLSetNameField, RPSLEmailField, RPSLDNSNameField, RPSLGenericNameField, RPSLReferenceField, - RPSLReferenceListField, RPSLTextField, RPSLAuthField, RPSLRouteSetMemberField) + RPSLReferenceListField, RPSLTextField, RPSLAuthField, RPSLRouteSetMemberField, RPSLChangedField) from ..parser_state import RPSLParserMessages @@ -285,6 +285,21 @@ def test_validate_email_field(): assert_validation_err('Invalid e-mail', field.parse, 'a@[192.0.2.2.2]') +def test_validate_changed_field(): + field = RPSLChangedField() + messages = RPSLParserMessages() + assert field.parse('foo.bar@example.asia', messages).value == 'foo.bar@example.asia' + assert field.parse('foo.bar@[192.0.2.1] 20190701', messages).value == 'foo.bar@[192.0.2.1] 20190701' + assert field.parse('foo.bar@[2001:db8::1] 19980101', messages).value == 'foo.bar@[2001:db8::1] 19980101' + assert not messages.errors() + + assert_validation_err('Invalid e-mail', field.parse, 'foo.bar+baz@') + assert_validation_err('Invalid changed date', field.parse, 'foo.bar@example.com 20191301') + assert_validation_err('Invalid e-mail', field.parse, '\nfoo.bar@example.com \n20190701') + assert_validation_err('Invalid changed date', field.parse, 'foo.bar@example.com \n20190701') + assert_validation_err('Invalid changed date', field.parse, 'foo.bar@example.com 20190701\n') + + def test_validate_dns_name_field(): field = RPSLDNSNameField() messages = RPSLParserMessages() diff --git a/irrd/rpsl/tests/test_rpsl_objects.py b/irrd/rpsl/tests/test_rpsl_objects.py index 600cc4807..c88e5c558 100644 --- a/irrd/rpsl/tests/test_rpsl_objects.py +++ b/irrd/rpsl/tests/test_rpsl_objects.py @@ -1,3 +1,5 @@ +import datetime + import pytest from IPy import IP from pytest import raises @@ -478,3 +480,36 @@ def test_parse(self): ] assert obj.references_strong_inbound() == set() assert obj.render_rpsl_text() == rpsl_text + + +class TestOverwriteDateNewChangedAttributes: + expected_date = datetime.datetime.now().strftime('%Y%m%d') + + # This applies to all objects identically - only one test needed + def test_changed_line_overwrite_with_date_and_comment(self): + new_rpsl_text = self._generate_old_new_object('changed: new1@example.com 19980101 # comment') + assert 'changed: changed@example.com 20190701 # comment' in new_rpsl_text + assert f'changed: new1@example.com {self.expected_date} # comment' in new_rpsl_text + + def test_changed_line_overwrite_without_comment(self): + new_rpsl_text = self._generate_old_new_object('changed: new1@example.com 19980101') + assert 'changed: changed@example.com 20190701 # comment' in new_rpsl_text + assert f'changed: new1@example.com {self.expected_date}' in new_rpsl_text + + def test_changed_line_overwrite_without_date_with_comment(self): + new_rpsl_text = self._generate_old_new_object('changed: new1@example.com#comment') + assert 'changed: changed@example.com 20190701 # comment' in new_rpsl_text + assert f'changed: new1@example.com {self.expected_date} # comment' in new_rpsl_text + + def _generate_old_new_object(self, new_changed_line): + rpsl_text = object_sample_mapping[RPSLRouteSet().rpsl_object_class] + obj_current = rpsl_object_from_text(rpsl_text, strict_validation=True) + + lines = rpsl_text.splitlines() + lines.insert(4, new_changed_line) + rpsl_text = '\n'.join(lines) + obj_new = rpsl_object_from_text(rpsl_text, strict_validation=True) + + obj_new.overwrite_date_new_changed_attributes(obj_current) + assert f'Set date in changed line' in obj_new.messages.infos()[1] + return obj_new.render_rpsl_text() diff --git a/irrd/scripts/tests/test_rpsl_read.py b/irrd/scripts/tests/test_rpsl_read.py index ca2597903..68d8c16ea 100644 --- a/irrd/scripts/tests/test_rpsl_read.py +++ b/irrd/scripts/tests/test_rpsl_read.py @@ -12,7 +12,7 @@ descr: TEST ASN block remarks: test remark mnt-by: TEST-MNT -changed: 2014-02-24T13:15:13Z +changed: changed@example.com 20190701 # comment tech-c: PERSON-TEST admin-c: PERSON-TEST source: TEST @@ -22,7 +22,7 @@ remarks: test remark mnt-by: TEST-MNT unknown-obj: unknown value should be caught in strict validation -changed: 2014-02-24T13:15:13Z +changed: changed@example.com 20190701 # comment tech-c: PERSON-TEST admin-c: PERSON-TEST source: TEST @@ -31,7 +31,7 @@ descr: TEST ASN block remarks: test remark mnt-by: TEST-MNT -changed: 2014-02-24T13:15:13Z +changed: changed@example.com 20190701 tech-c: PERSON-TEST admin-c: PERSON-TEST source: TEST diff --git a/irrd/updates/parser.py b/irrd/updates/parser.py index 4bc978423..5fbc57e87 100644 --- a/irrd/updates/parser.py +++ b/irrd/updates/parser.py @@ -125,6 +125,10 @@ def save(self, database_handler: DatabaseHandler) -> None: logger.info(f'{id(self)}: Saving change for {self.rpsl_obj_new}: deleting current object') database_handler.delete_rpsl_object(self.rpsl_obj_current) else: + if not self.used_override: + self.rpsl_obj_new.overwrite_date_new_changed_attributes(self.rpsl_obj_current) + # This call may have emitted a new info message. + self._import_new_rpsl_obj_info_messages() logger.info(f'{id(self)}: Saving change for {self.rpsl_obj_new}: inserting/updating current object') database_handler.upsert_rpsl_object(self.rpsl_obj_new) self.status = UpdateRequestStatus.SAVED @@ -138,7 +142,10 @@ def submitter_report(self) -> str: report = f'{self.request_type_str().title()} {status}: [{self.object_class_str()}] {self.object_pk_str()}\n' if self.info_messages or self.error_messages: - report += '\n' + self.rpsl_text_submitted + '\n' + if not self.rpsl_obj_new or self.error_messages: + report += '\n' + self.rpsl_text_submitted + '\n' + else: + report += '\n' + self.rpsl_obj_new.render_rpsl_text() + '\n' report += ''.join([f'ERROR: {e}\n' for e in self.error_messages]) report += ''.join([f'INFO: {e}\n' for e in self.info_messages]) return report @@ -251,6 +258,17 @@ def _check_references(self) -> bool: logger.debug(f'{id(self)}: Reference check succeeded') return True + def _import_new_rpsl_obj_info_messages(self): + """ + Import new info messages from self.rpsl_obj_new. + This is used after overwrite_date_new_changed_attributes() + is called, as it's called just before saving, but may + emit a new info message. + """ + for info_message in self.rpsl_obj_new.messages.infos(): + if info_message not in self.info_messages: + self.info_messages.append(info_message) + def parse_change_requests(requests_text: str, database_handler: DatabaseHandler, diff --git a/irrd/updates/tests/test_handler.py b/irrd/updates/tests/test_handler.py index ba770b861..e546d1a9c 100644 --- a/irrd/updates/tests/test_handler.py +++ b/irrd/updates/tests/test_handler.py @@ -1,5 +1,5 @@ # flake8: noqa: W293 - +import datetime import textwrap from unittest.mock import Mock @@ -28,6 +28,7 @@ def prepare_mocks(monkeypatch, config_override): class TestChangeSubmissionHandler: # NOTE: the scope of this test also includes ChangeRequest, ReferenceValidator and AuthValidator - # this is more of an update handler integration test. + expected_changed_date = datetime.datetime.now().strftime('%Y%m%d') def test_parse_valid_new_objects_with_override(self, prepare_mocks): mock_dq, mock_dh, mock_email = prepare_mocks @@ -40,7 +41,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST mntner: TEST-MNT @@ -49,7 +50,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): auth: PGPKey-80F238C6 auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST override: override-password @@ -63,7 +64,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """) @@ -101,7 +102,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): Delete: 0 DETAILED EXPLANATION: - + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- Create succeeded: [person] PERSON-TEST @@ -112,7 +113,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): --- Create succeeded: [inetnum] 80.16.151.184 - 80.16.151.191 - inetnum: 80.16.151.184 - 80.016.151.191 + inetnum: 80.16.151.184 - 80.16.151.191 netname: NETECONOMY-MG41731 descr: TELECOM ITALIA LAB SPA country: IT @@ -121,7 +122,7 @@ def test_parse_valid_new_objects_with_override(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark @@ -140,7 +141,7 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -151,7 +152,7 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): mnt-nfy: mnt-nfy@example.com auth: PGPKey-80F238C6 mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST """) rpsl_text = person_text + '\n\n' + mntner_text @@ -183,7 +184,7 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): assert mock_dh.mock_calls[2][0] == 'commit' assert mock_dh.mock_calls[3][0] == 'close' - assert handler.submitter_report() == textwrap.dedent(""" + assert handler.submitter_report() == textwrap.dedent(f""" > Message-ID: test > From: example@example.com @@ -201,18 +202,29 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): Delete: 0 DETAILED EXPLANATION: - + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- Create succeeded: [person] PERSON-TEST - + + person: Placeholder Person Object + address: The Netherlands + phone: +31 20 000 0000 + nic-hdl: PERSON-TEST + mnt-by: TEST-MNT + e-mail: email@example.com + changed: changed@example.com {self.expected_changed_date} # comment + source: TEST + + INFO: Set date in changed line "changed@example.com 20190701 # comment" to today. + --- Modify succeeded: [mntner] TEST-MNT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """) - expected_notification = textwrap.dedent(""" + expected_notification = textwrap.dedent(f""" This is to notify you of changes in the TEST database or object authorisation failures. @@ -241,7 +253,7 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com {self.expected_changed_date} # comment source: TEST --- @@ -257,7 +269,7 @@ def test_parse_valid_new_person_existing_mntner_pgp_key(self, prepare_mocks): mnt-nfy: mnt-nfy@example.com auth: PGPKey-80F238C6 mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -278,7 +290,7 @@ def test_parse_invalid_new_objects_pgp_key_does_not_exist(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -288,7 +300,7 @@ def test_parse_invalid_new_objects_pgp_key_does_not_exist(self, prepare_mocks): upd-to: unread@ripe.net auth: PGPKey-80F238C6 mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST """) rpsl_text = person_text + '\n\n' + mntner_text @@ -327,7 +339,7 @@ def test_parse_valid_delete(self, prepare_mocks): mnt-by: TEST-MNT e-mail: email@example.com notify: notify@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -401,7 +413,7 @@ def test_parse_valid_delete(self, prepare_mocks): mnt-by: TEST-MNT e-mail: email@example.com notify: notify@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -428,7 +440,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): auth: PGPKey-80F238C6 auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST override: override-password @@ -442,7 +454,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark @@ -452,7 +464,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: OTHER-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -499,7 +511,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): auth: PGPKey-80F238C6 auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Object PERSON-TEST referenced in field admin-c not found in database TEST - must reference one of role, person. @@ -516,7 +528,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark @@ -533,7 +545,7 @@ def test_parse_invalid_cascading_failure(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: OTHER-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Object OTHER-MNT referenced in field mnt-by not found in database TEST - must reference mntner. @@ -556,7 +568,7 @@ def test_parse_invalid_single_failure_invalid_password(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -597,7 +609,7 @@ def test_parse_invalid_single_failure_invalid_password(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Authorisation for person PERSON-TEST failed: must by authenticated by one of: TEST-MNT @@ -631,7 +643,7 @@ def test_parse_invalid_single_failure_invalid_password(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -663,7 +675,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST mntner: TEST-MNT @@ -672,7 +684,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): auth: PGPKey-80F238C6 auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST password: wrong-password @@ -686,7 +698,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """) @@ -733,7 +745,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Authorisation for person PERSON-TEST failed: must by authenticated by one of: TEST-MNT @@ -747,7 +759,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): auth: PGPKey-80F238C6 auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT - changed: 2016-10-05T10:41:15Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Authorisation for mntner TEST-MNT failed: must by authenticated by one of: TEST-MNT @@ -766,7 +778,7 @@ def test_parse_invalid_cascading_failure_invalid_password(self, prepare_mocks): status: ASSIGNED PA notify: neteconomy.rete@telecomitalia.it mnt-by: TEST-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark @@ -783,7 +795,7 @@ def test_parse_invalid_object_syntax(self, prepare_mocks): rpsl_text = textwrap.dedent(""" person: Placeholder Person Object nic-hdl: PERSON-TEST - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST """) @@ -815,7 +827,7 @@ def test_parse_invalid_object_syntax(self, prepare_mocks): person: Placeholder Person Object nic-hdl: PERSON-TEST - changed: 2009-07-24T17:00:00Z + changed: changed@example.com 20190701 # comment source: TEST ERROR: Mandatory attribute "address" on object person is missing diff --git a/irrd/updates/tests/test_parser.py b/irrd/updates/tests/test_parser.py index 9f0d4fca5..71931b40e 100644 --- a/irrd/updates/tests/test_parser.py +++ b/irrd/updates/tests/test_parser.py @@ -714,7 +714,7 @@ def test_user_report(self, prepare_mocks): tech-c: PERSON-TEST status: ASSIGNED PA mnt-by: test-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """).strip() + '\n' @@ -730,7 +730,7 @@ def test_user_report(self, prepare_mocks): admin-c: PERSON-TEST notify: notify@example.com mnt-by: TEST-MNT - changed: 2017-05-19T12:22:08Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """).strip() + '\n' @@ -750,7 +750,7 @@ def test_user_report(self, prepare_mocks): +tech-c: NEW-TEST status: ASSIGNED PA mnt-by: test-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment New version of this object: @@ -763,7 +763,7 @@ def test_user_report(self, prepare_mocks): tech-c: NEW-TEST status: ASSIGNED PA mnt-by: test-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """).strip() + '\n' @@ -783,7 +783,7 @@ def test_user_report(self, prepare_mocks): +tech-c: NEW-TEST status: ASSIGNED PA mnt-by: test-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment *Rejected* new version of this object: @@ -796,7 +796,7 @@ def test_user_report(self, prepare_mocks): tech-c: NEW-TEST status: ASSIGNED PA mnt-by: test-MNT - changed: 2001-09-21T22:08:01Z + changed: changed@example.com 20190701 # comment source: TEST remarks: remark """).strip() + '\n' diff --git a/irrd/utils/rpsl_samples.py b/irrd/utils/rpsl_samples.py index 68c98848f..68578e6f3 100644 --- a/irrd/utils/rpsl_samples.py +++ b/irrd/utils/rpsl_samples.py @@ -5,7 +5,7 @@ descr: TEST ASN block remarks: test remark mnt-by: TEST-MNT -changed: 2014-02-24T13:15:13Z +changed: changed@example.com # only changed line without date tech-c: PERSON-TEST admin-c: PERSON-TEST source: TEST @@ -20,7 +20,7 @@ admin-c: PERSON-TEST notify: notify@example.com mnt-by: TEST-MNT -changed: 2017-05-19T12:22:08Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -64,7 +64,7 @@ admin-c: PERSON-TEST tech-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2018-03-13T19:16:16Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -74,7 +74,7 @@ tech-c: PERSON-TEST zone-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2011-02-04T10:33:38Z +changed: changed@example.com 20190701 # comment source: TEST # foo remarks: remark @@ -173,7 +173,7 @@ admin-c: PERSON-TEST tech-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2002-12-04T11:34:27Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -185,7 +185,7 @@ admin-c: PERSON-TEST tech-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2001-09-21T22:07:57Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -197,7 +197,7 @@ tech-c: PERSON-TEST mnt-by: TEST-MNT status: ASSIGNED -changed: 2011-10-14T15:05:09Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -211,7 +211,7 @@ tech-c: PERSON-TEST status: ASSIGNED PA mnt-by: test-MNT -changed: 2001-09-21T22:08:01Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -489,7 +489,7 @@ admin-c: PERSON-TEST tech-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2018-04-10T13:39:39Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -504,7 +504,7 @@ auth: MD5-pw $1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM. # md5-password mnt-by: TEST-MNT mnt-by: OTHER1-MNT,OTHER2-MNT -changed: 2016-10-05T10:41:15Z +changed: changed@example.com 20190701 # comment remarks: unįcöde tæst 🌈🦄 source: TEST remarks: remark @@ -519,7 +519,7 @@ admin-c: PERSON-TEST notify: hostmaster@dnt.ro mnt-by: TEST-MNT -changed: 2001-09-21T23:07:39Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -533,7 +533,7 @@ mnt-by: TEST-MNT e-mail: email@example.com notify: notify@example.com -changed: 2009-07-24T17:00:00Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -547,7 +547,7 @@ nic-hdl: ROLE-TEST notify: notify@example.com mnt-by: TEST-MNT -changed: 2017-11-21T15:56:58Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -559,7 +559,7 @@ origin: AS65537 member-of: RS-TEST mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -571,7 +571,7 @@ admin-c: PERSON-TEST mnt-by: TEST-MNT mp-members: 2001:0dB8::/48 -changed: 2001-09-22T09:34:03Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -580,7 +580,7 @@ descr: test route6 origin: AS65537 mnt-by: test-MNT -changed: 2004-12-29T21:30:40Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -593,7 +593,7 @@ tech-c: PERSON-TEST admin-c: PERSON-TEST mnt-by: TEST-MNT -changed: 2001-09-22T09:34:04Z +changed: changed@example.com 20190701 # comment source: TEST remarks: remark """ @@ -601,7 +601,7 @@ SAMPLE_UNKNOWN_CLASS = """foo-block: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -609,7 +609,7 @@ origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -617,14 +617,14 @@ SAMPLE_LEGACY_IRRD_ARTIFACT = """*xxte: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ SAMPLE_MALFORMED_ATTRIBUTE_NAME = """route: 192.0.2.0/24 origin: AS65537 $$$-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -638,14 +638,14 @@ origin: AS65537 mnt-by: TEST-MNT foo: bar -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ SAMPLE_INVALID_MULTIPLE_ATTRIBUTE = """route: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST source: NOT-TEST """ @@ -653,28 +653,28 @@ SAMPLE_MALFORMED_PK = """route: not-a-prefix origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ SAMPLE_MALFORMED_SOURCE = """route: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: +TEST$$$ """ SAMPLE_MISSING_SOURCE = """route: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment """ SAMPLE_LINE_NEITHER_CONTINUATION_NOR_ATTR = """route: 192.0.2.0/24 origin: AS65537 mnt-by: TEST-MNT or -changed: 2009-10-15T09:32:17Z +changed: changed@example.com 20190701 # comment source: TEST """ @@ -766,23 +766,23 @@ nic-hdl: PERSON-TEST mnt-by: TEST-MNT e-mail: email@example.com -changed: 2009-07-24T17:00:00Z +changed: changed@example.com 20190701 # comment source: TEST -----BEGIN PGP SIGNATURE----- -iQIzBAEBCAAdFiEEhiYdjb69pPVGktZNqDg7p4DyOMYFAlw92E0ACgkQqDg7p4Dy -OMaGSw//cSk+Ds2A8hdAuZ7oShHaOMBqnqIikv32PjXq9PYs9ITS8/r6eLULTyYF -/MXMSUG7/sn2qqb13radb8D+4NpfVwLu+ht5rLPhJZLn2GA6685vvK/kk1rRU7EM -SpVhayggnvL1IB6hRbFGpyFOZ/GAnJ70dH8XNte3EWiqCl+ata/LTeOd335rxSOb -nn1Vi+cQ38GXFW3wxcbgm3Sq81FNXbRJRczCc7gDph9j5eAsolS6ZvJa6tSijiW7 -XzRGhFpR/BHRXzqf/kg+9yjxZgNXpvVgHlMJH6UdYFo4liUA2Tyn/Qt0HMB44bDR -FYuQqi1wKfBGm/nRZkye07M7Hry8gG52aRUK1NG3KC32GRuZ5NkA3pF+d+aGxkIV -atGObcjc+rm6mqmIxnfm+D/+M1c4M419udqtDLLyO5wYlMHMmZZV+OxEA589eWhA -/B3wxBO8k+JbrPII1onTf0+8euO+nTA7ZGkhBaZ6nnoMaLYmznkOM+Ezj+jOlX42 -DZdklZqvro07uvvUh+mXcDMVABt37HnPEOQH5ib6zGFIY6PHP16th+qRPgcB/3h7 -VbheBw40Gf7tS4hUg3jzFKqU0IVW5QaY6NATPvVkE99QXjEUA/DwDZJBqOfqSa5L -fqXVFwOtGh6U7r5FbwAmxr50oVLjFR3+q4ykVoghzImnX1//1wY= -=H27q +iQIzBAEBCAAdFiEEhiYdjb69pPVGktZNqDg7p4DyOMYFAl0jNUQACgkQqDg7p4Dy +OMabkBAAq5gCcytpw9UwXMOkLXS2Fd0QfgVRpnc6l0aBmi7uWU6xWe72GU+LaNcC +1vi/OQqT38hXKtDmPMeItPhy5MHanagZZL+ZtVK3SGUaG3rV560Mna2sEnNTeJcb +OvMEg8JXDUP3O4T3kNudTCnBj2d1JhQUNfm7CivWMFe9dxfw+rvirzUnWlhKUZY/ +93FGZ1/FpjlAOrLpcFTdvBXajBgpCCHTyTSWBs2KuR2gEWOzIzkyWXIHupwym671 +nTg++M/ziPyYJXDv7PqKiBU3DnvSAAOialhk1fse9YW1Dj2dcHz8s2Ex8gv2TcLi +9e6gCF2rOBnusO3yVcqKNBEbpqB+wCbLPGG1C+8n177opTxUipm5kadDBRQy/sZA +P6740cd5Jky1gzWDykch+8ttd8MNVFoNotk1MpauU2zP5/agPuDJuoF6RbCMqX62 +MsWp+9c1rlNNUTgQfqxTaEZ+oIj/mLK36iiMQzy0ey9GT/Viuow2WYDjDI5P+OB2 +mZp0grLzKK07KdTf/+1WYZb09GhSYzPlKyg12KP3Zoaklh83uYn06mqaeN6YEXYE +zwBzW+p+qqN0rNRMFTNy3WnVVzZY5UWljU83jMBQkXiOSxo72/yIpG89xzi24Bqp ++pewy9PIcK1JBKvGyeO2Gh1K2tsrVYzs7aP5/RmkmUyrQeXa3l4= +=FLEo -----END PGP SIGNATURE----- """