def test_check_auth_invalid_update_mntner_wrong_password_current_db_object( self, prepare_mocks): mock_dq, mock_dh = prepare_mocks # Make the crypt password invalid in the version from the mock database mock_dh.execute_query = lambda query: [{ 'object_text': SAMPLE_MNTNER.replace('CRYPT-PW', 'FAILED') }] reference_validator = ReferenceValidator(mock_dh) auth_validator = AuthValidator(mock_dh) # This password is valid for the new object, but invalid for the current version in the DB result_mntner = parse_update_requests( SAMPLE_MNTNER + 'password: crypt-password', mock_dh, auth_validator, reference_validator)[0] auth_validator.pre_approve([result_mntner]) assert not result_mntner._check_auth() assert result_mntner.error_messages == [ 'Authorisation for mntner TEST-MNT failed: must by authenticated by one of: TEST-MNT, ' 'OTHER1-MNT, OTHER2-MNT' ] assert flatten_mock_calls(mock_dq) == [ ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pk', ('TEST-MNT', ), {}], ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pks', (['TEST-MNT', 'OTHER1-MNT', 'OTHER2-MNT'], ), {}], ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pks', (['TEST-MNT', 'OTHER1-MNT', 'OTHER2-MNT'], ), {}], ]
def test_check_auth_invalid_update_mntner_submits_new_object_with_dummy_hash_multiple_passwords( self, prepare_mocks): mock_dq, mock_dh = prepare_mocks mock_dh.execute_query = lambda query: [{'object_text': SAMPLE_MNTNER}] reference_validator = ReferenceValidator(mock_dh) auth_validator = AuthValidator(mock_dh) # Submit the mntner with dummy password values as would be returned by queries, # but multiple password attributes, which means we wouldn't know which password to set. data = SAMPLE_MNTNER.replace('LEuuhsBJNFV0Q', PASSWORD_HASH_DUMMY_VALUE) data = data.replace('$1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM.', PASSWORD_HASH_DUMMY_VALUE) result_mntner = parse_change_requests( data + 'password: md5-password\npassword: other-password', mock_dh, auth_validator, reference_validator)[0] auth_validator.pre_approve([result_mntner]) result_mntner._check_auth() assert not result_mntner.is_valid() assert result_mntner.error_messages == [ 'Object submitted with dummy hash values, but multiple or no passwords submitted. ' 'Either submit only full hashes, or a single password.' ]
def test_check_auth_invalid_create_mntner_referencing_self_with_dummy_passwords( self, prepare_mocks): mock_dq, mock_dh = prepare_mocks mock_dh.execute_query = lambda query: [] reference_validator = ReferenceValidator(mock_dh) auth_validator = AuthValidator(mock_dh) # Submit the mntner with dummy password values as would be returned by queries. # This should not be allowed in new objects. data = SAMPLE_MNTNER.replace('LEuuhsBJNFV0Q', PASSWORD_HASH_DUMMY_VALUE) data = data.replace('$1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM.', PASSWORD_HASH_DUMMY_VALUE) result_mntner = parse_update_requests( data + 'password: crypt-password', mock_dh, auth_validator, reference_validator)[0] auth_validator.pre_approve([result_mntner]) assert not result_mntner._check_auth() assert result_mntner.error_messages == [ 'Authorisation failed for the auth methods on this mntner object.' ] assert flatten_mock_calls(mock_dq) == [ ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pk', ('TEST-MNT', ), {}], ]
def test_non_authorative_source(self, prepare_mocks): mock_dq, mock_dh = prepare_mocks mock_dh.execute_query = lambda query: [] auth_validator = AuthValidator(mock_dh) result = parse_change_requests(SAMPLE_MNTNER.replace('TEST', 'TEST2'), mock_dh, auth_validator, None)[0] assert result.status == UpdateRequestStatus.ERROR_NON_AUTHORITIVE assert not result.is_valid() assert result.error_messages == [ 'This instance is not authoritative for source TEST2' ]
def test_check_auth_valid_update_mntner_submits_new_object_with_all_dummy_hash_values( self, prepare_mocks): mock_dq, mock_dh = prepare_mocks mock_dh.execute_query = lambda query: [{'object_text': SAMPLE_MNTNER}] reference_validator = ReferenceValidator(mock_dh) auth_validator = AuthValidator(mock_dh) # Submit the mntner with dummy password values as would be returned by queries, # but a password attribute that is valid for the current DB object. data = SAMPLE_MNTNER.replace('LEuuhsBJNFV0Q', PASSWORD_HASH_DUMMY_VALUE) data = data.replace('$1$fgW84Y9r$kKEn9MUq8PChNKpQhO6BM.', PASSWORD_HASH_DUMMY_VALUE) result_mntner = parse_change_requests( data + 'password: crypt-password', mock_dh, auth_validator, reference_validator)[0] auth_validator.pre_approve([result_mntner]) assert result_mntner._check_auth() assert not result_mntner.error_messages assert result_mntner.info_messages == [ 'As you submitted dummy hash values, all password hashes on this object ' 'were replaced with a new MD5-PW hash of the password you provided for ' 'authentication.' ] auth_pgp, auth_hash = splitline_unicodesafe( result_mntner.rpsl_obj_new.parsed_data['auth']) assert auth_pgp == 'PGPKey-80F238C6' assert auth_hash.startswith('MD5-PW ') assert md5_crypt.verify('crypt-password', auth_hash[7:]) assert auth_hash in result_mntner.rpsl_obj_new.render_rpsl_text() assert flatten_mock_calls(mock_dq) == [ ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pk', ('TEST-MNT', ), {}], ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pks', ({'OTHER1-MNT', 'OTHER2-MNT', 'TEST-MNT'}, ), {}], ['sources', (['TEST'], ), {}], ['object_classes', (['mntner'], ), {}], ['rpsl_pks', ({'OTHER1-MNT', 'OTHER2-MNT'}, ), {}], ]
def test_check_auth_invalid_update_mntner_submits_new_object_with_mixed_dummy_hash_real_hash( self, prepare_mocks): mock_dq, mock_dh = prepare_mocks mock_dh.execute_query = lambda query: [{'object_text': SAMPLE_MNTNER}] reference_validator = ReferenceValidator(mock_dh) auth_validator = AuthValidator(mock_dh) # Submit the mntner with dummy password values as would be returned by queries, # but a password attribute that is valid for the current DB object. data = SAMPLE_MNTNER.replace('LEuuhsBJNFV0Q', PASSWORD_HASH_DUMMY_VALUE) result_mntner = parse_change_requests(data + 'password: md5-password', mock_dh, auth_validator, reference_validator)[0] auth_validator.pre_approve([result_mntner]) assert not result_mntner.is_valid() assert result_mntner.error_messages == [ 'Either all password auth hashes in a submitted mntner must be dummy objects, or none.', ]
IRRD_ROOT_PATH = str(Path(__file__).resolve().parents[2]) sys.path.append(IRRD_ROOT_PATH) AS_SET_REFERRING_OTHER_SET = """as-set: AS-TESTREF descr: description members: AS-SETTEST, AS65540 tech-c: PERSON-TEST admin-c: PERSON-TEST notify: [email protected] mnt-by: TEST-MNT changed: 2017-05-19T12:22:08Z source: TEST remarks: remark """ SAMPLE_MNTNER_CLEAN = SAMPLE_MNTNER.replace( 'mnt-by: OTHER1-MNT,OTHER2-MNT\n', '') LARGE_UPDATE = '\n\n'.join([ SAMPLE_AS_BLOCK, SAMPLE_AS_SET, SAMPLE_AUT_NUM, SAMPLE_AUT_NUM.replace('aut-num: as065537', 'aut-num: as65538'), SAMPLE_AUT_NUM.replace('aut-num: as065537', 'aut-num: as65539'), SAMPLE_AUT_NUM.replace('aut-num: as065537', 'aut-num: as65540'), SAMPLE_DOMAIN, SAMPLE_FILTER_SET, SAMPLE_INET_RTR, SAMPLE_INET6NUM, SAMPLE_INETNUM, SAMPLE_KEY_CERT, SAMPLE_MNTNER_CLEAN, SAMPLE_PEERING_SET,
from irrd.storage.database_handler import DatabaseHandler from irrd.utils.rpsl_samples import (SAMPLE_AS_SET, SAMPLE_FILTER_SET, SAMPLE_MNTNER, SAMPLE_MNTNER_CRYPT, SAMPLE_MNTNER_MD5, SAMPLE_PERSON, SAMPLE_ROUTE, SAMPLE_ROUTE6) from irrd.utils.test_utils import flatten_mock_calls from irrd.utils.text import remove_auth_hashes from irrd.storage.queries import RPSLDatabaseSuspendedQuery from irrd.updates.parser_state import UpdateRequestType from ..validators import AuthValidator, RulesValidator VALID_PW = 'override-password' INVALID_PW = 'not-override-password' VALID_PW_HASH = '$1$J6KycItM$MbPaBU6iFSGFV299Rk7Di0' MNTNER_OBJ_CRYPT_PW = SAMPLE_MNTNER.replace('MD5', '') MNTNER_OBJ_MD5_PW = SAMPLE_MNTNER.replace('CRYPT', '') class TestAuthValidator: @pytest.fixture() def prepare_mocks(self, monkeypatch, config_override): mock_dh = Mock() mock_dq = Mock() monkeypatch.setattr('irrd.updates.validators.RPSLDatabaseQuery', lambda: mock_dq) config_override({ 'auth': { 'password_hashers': { 'crypt-pw': 'enabled'