def check_references_from_others(self, rpsl_obj: RPSLObject) -> ValidatorResult: """ Check for any references to this object in the DB. Used for validating deletions. Checks self._preload_deleted, because a reference from an object that is also about to be deleted, is acceptable. """ result = ValidatorResult() if not rpsl_obj.references_strong_inbound(): return result query = RPSLDatabaseQuery().sources([rpsl_obj.source()]) query = query.lookup_attrs_in(rpsl_obj.references_strong_inbound(), [rpsl_obj.pk()]) query_results = self.database_handler.execute_query(query) for query_result in query_results: reference_to_be_deleted = ( query_result['object_class'], query_result['rpsl_pk'], query_result['source']) in self._preloaded_deleted if not reference_to_be_deleted: result.error_messages.add( f'Object {rpsl_obj.pk()} to be deleted, but still referenced ' f'by {query_result["object_class"]} {query_result["rpsl_pk"]}' ) return result
def validate(self, rpsl_obj: RPSLObject, request_type: UpdateRequestType) -> ValidatorResult: result = ValidatorResult() if request_type == UpdateRequestType.CREATE and rpsl_obj.rpsl_object_class == 'mntner' and \ self._check_suspended_mntner_with_same_pk(rpsl_obj.pk(), rpsl_obj.source()): result.error_messages.add( f'A suspended mntner with primary key {rpsl_obj.pk()} already exists for {rpsl_obj.source()}' ) return result
def validate_rpsl_object( self, rpsl_object: RPSLObject) -> Tuple[ScopeFilterStatus, str]: """ Validate whether an RPSLObject is in scope. Returns a tuple of a ScopeFilterStatus and an explanation string. """ return self._validate_rpsl_data( rpsl_object.source(), rpsl_object.rpsl_object_class, rpsl_object.prefix, rpsl_object.asn_first, )
def check_auth(self, rpsl_obj_new: RPSLObject, rpsl_obj_current: Optional[RPSLObject]) -> ValidatorResult: """ Check whether authentication passes for all required objects. """ source = rpsl_obj_new.source() result = ValidatorResult() mntners_new = rpsl_obj_new.parsed_data['mnt-by'] logger.debug( f'Checking auth for {rpsl_obj_new}, mntners in new object: {mntners_new}' ) if not self._check_mntners(mntners_new, source): self._generate_failure_message(result, mntners_new, rpsl_obj_new) if rpsl_obj_current: mntners_current = rpsl_obj_current.parsed_data['mnt-by'] logger.debug( f'Checking auth for {rpsl_obj_current}, mntners in new object: {mntners_current}' ) if not self._check_mntners(mntners_current, source): self._generate_failure_message(result, mntners_current, rpsl_obj_new) if isinstance(rpsl_obj_new, RPSLMntner): # Dummy auth values are only permitted in existing objects, which are never pre-approved. if rpsl_obj_new.has_dummy_auth_value() and rpsl_obj_new.pk( ) not in self._pre_approved: if len(self.passwords) == 1: logger.debug( f'Object {rpsl_obj_new} submitted with dummy hash values and single password, ' f'replacing all hashes with currently supplied password.' ) rpsl_obj_new.force_single_new_password(self.passwords[0]) result.info_messages.add( '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.') else: result.error_messages.add( f'Object submitted with dummy hash values, but multiple or no passwords ' f'submitted. Either submit all full hashes, or a single password.' ) elif not rpsl_obj_new.verify_auth(self.passwords, self.keycert_obj_pk): result.error_messages.add( f'Authorisation failed for the auth methods on this mntner object.' ) return result
def check_references_to_others(self, rpsl_obj: RPSLObject) -> ValidatorResult: """ Check the validity of references of a particular object, i.e. whether all references to other objects actually exist in the database. """ result = ValidatorResult() references = rpsl_obj.referred_strong_objects() source = rpsl_obj.source() for field_name, objects_referred, object_pks in references: for object_pk in object_pks: if not self._check_reference_to_others(objects_referred, object_pk, source): if len(objects_referred) > 1: objects_referred_str = 'one of ' + ', '.join(objects_referred) else: objects_referred_str = objects_referred[0] result.error_messages.add(f'Object {object_pk} referenced in field {field_name} not found in ' f'database {source} - must reference {objects_referred_str}.') return result
def process_auth(self, rpsl_obj_new: RPSLObject, rpsl_obj_current: Optional[RPSLObject]) -> ValidatorResult: """ Check whether authentication passes for all required objects. Returns a ValidatorResult object with error/info messages, and fills result.mntners_notify with the RPSLMntner objects that may have to be notified. If a valid override password is provided, changes are immediately approved. On the result object, used_override is set to True, but mntners_notify is not filled, as mntner resolving does not take place. """ source = rpsl_obj_new.source() result = ValidatorResult() override_hash = get_setting('auth.override_password') if override_hash: for override in self.overrides: try: if md5_crypt.verify(override, override_hash): result.used_override = True logger.debug(f'Found valid override password.') return result else: logger.info(f'Found invalid override password, ignoring.') except ValueError as ve: logger.error(f'Exception occurred while checking override password: {ve} (possible misconfigured hash?)') elif self.overrides: logger.info(f'Ignoring override password, auth.override_password not set.') mntners_new = rpsl_obj_new.parsed_data['mnt-by'] logger.debug(f'Checking auth for new object {rpsl_obj_new}, mntners in new object: {mntners_new}') valid, mntner_objs_new = self._check_mntners(mntners_new, source) if not valid: self._generate_failure_message(result, mntners_new, rpsl_obj_new) if rpsl_obj_current: mntners_current = rpsl_obj_current.parsed_data['mnt-by'] logger.debug(f'Checking auth for current object {rpsl_obj_current}, ' f'mntners in new object: {mntners_current}') valid, mntner_objs_current = self._check_mntners(mntners_current, source) if not valid: self._generate_failure_message(result, mntners_current, rpsl_obj_new) result.mntners_notify = mntner_objs_current else: result.mntners_notify = mntner_objs_new if isinstance(rpsl_obj_new, RPSLMntner): if not rpsl_obj_current: result.error_messages.add('New mntner objects must be added by an administrator.') return result # Dummy auth values are only permitted in existing objects if rpsl_obj_new.has_dummy_auth_value(): if len(self.passwords) == 1: logger.debug(f'Object {rpsl_obj_new} submitted with dummy hash values and single password, ' f'replacing all hashes with currently supplied password.') rpsl_obj_new.force_single_new_password(self.passwords[0]) result.info_messages.add('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.') else: result.error_messages.add(f'Object submitted with dummy hash values, but multiple or no passwords ' f'submitted. Either submit only full hashes, or a single password.') elif not rpsl_obj_new.verify_auth(self.passwords, self.keycert_obj_pk): result.error_messages.add(f'Authorisation failed for the auth methods on this mntner object.') return result
def _init_related_object_query(rpsl_object_class: str, rpsl_obj_new: RPSLObject) -> RPSLDatabaseQuery: query = RPSLDatabaseQuery().sources([rpsl_obj_new.source()]) query = query.object_classes([rpsl_object_class]) return query.first_only()
def process_auth( self, rpsl_obj_new: RPSLObject, rpsl_obj_current: Optional[RPSLObject]) -> ValidatorResult: """ Check whether authentication passes for all required objects. Returns a ValidatorResult object with error/info messages, and fills result.mntners_notify with the RPSLMntner objects that may have to be notified. If a valid override password is provided, changes are immediately approved. On the result object, used_override is set to True, but mntners_notify is not filled, as mntner resolving does not take place. """ source = rpsl_obj_new.source() result = ValidatorResult() if self.check_override(): result.used_override = True logger.debug('Found valid override password.') return result mntners_new = rpsl_obj_new.parsed_data['mnt-by'] logger.debug( f'Checking auth for new object {rpsl_obj_new}, mntners in new object: {mntners_new}' ) valid, mntner_objs_new = self._check_mntners(mntners_new, source) if not valid: self._generate_failure_message(result, mntners_new, rpsl_obj_new) if rpsl_obj_current: mntners_current = rpsl_obj_current.parsed_data['mnt-by'] logger.debug( f'Checking auth for current object {rpsl_obj_current}, ' f'mntners in new object: {mntners_current}') valid, mntner_objs_current = self._check_mntners( mntners_current, source) if not valid: self._generate_failure_message(result, mntners_current, rpsl_obj_new) result.mntners_notify = mntner_objs_current else: result.mntners_notify = mntner_objs_new mntners_related = self._find_related_mntners(rpsl_obj_new, result) if mntners_related: related_object_class, related_pk, related_mntner_list = mntners_related logger.debug( f'Checking auth for related object {related_object_class} / ' f'{related_pk} with mntners {related_mntner_list}') valid, mntner_objs_related = self._check_mntners( related_mntner_list, source) if not valid: self._generate_failure_message(result, related_mntner_list, rpsl_obj_new, related_object_class, related_pk) result.mntners_notify = mntner_objs_related if isinstance(rpsl_obj_new, RPSLMntner): if not rpsl_obj_current: result.error_messages.add( 'New mntner objects must be added by an administrator.') return result # Dummy auth values are only permitted in existing objects if rpsl_obj_new.has_dummy_auth_value(): if len(self.passwords) == 1: logger.debug( f'Object {rpsl_obj_new} submitted with dummy hash values and single password, ' f'replacing all hashes with currently supplied password.' ) rpsl_obj_new.force_single_new_password(self.passwords[0]) result.info_messages.add( 'As you submitted dummy hash values, all password hashes on this object ' 'were replaced with a new BCRYPT-PW hash of the password you provided for ' 'authentication.') else: result.error_messages.add( 'Object submitted with dummy hash values, but multiple or no passwords ' 'submitted. Either submit only full hashes, or a single password.' ) elif not rpsl_obj_new.verify_auth(self.passwords, self.keycert_obj_pk): result.error_messages.add( 'Authorisation failed for the auth methods on this mntner object.' ) return result