예제 #1
0
 def _prepare_query(self) -> RPSLDatabaseQuery:
     """Prepare an RPSLDatabaseQuery by applying relevant sources/class filters."""
     query = RPSLDatabaseQuery()
     if self.sources:
         query.sources(self.sources)
     else:
         default = get_setting('sources_default')
         if default:
             query.sources(list(default))
     if self.object_classes:
         query.object_classes(self.object_classes)
     return query
예제 #2
0
 def _prepare_query(self, column_names=None, ordered_by_sources=True) -> RPSLDatabaseQuery:
     """Prepare an RPSLDatabaseQuery by applying relevant sources/class filters."""
     query = RPSLDatabaseQuery(column_names, ordered_by_sources)
     if self.sources:
         query.sources(self.sources)
     else:
         default = get_setting('sources_default')
         if default:
             query.sources(list(default))
     if self.object_classes:
         query.object_classes(self.object_classes)
     return query
예제 #3
0
 def _prepare_query(self,
                    column_names=None,
                    ordered_by_sources=True) -> RPSLDatabaseQuery:
     """Prepare an RPSLDatabaseQuery by applying relevant sources/class filters."""
     query = RPSLDatabaseQuery(column_names, ordered_by_sources)
     if self.sources:
         query.sources(self.sources)
     if self.object_class_filter:
         query.object_classes(self.object_class_filter)
     if self.rpki_invalid_filter_enabled:
         query.rpki_status([RPKIStatus.not_found, RPKIStatus.valid])
     if self.out_scope_filter_enabled:
         query.scopefilter_status([ScopeFilterStatus.in_scope])
     self.object_class_filter = []
     return query
예제 #4
0
    def _check_mntners(self, mntner_pk_list: List[str], source: str) -> Tuple[bool, List[RPSLMntner]]:
        """
        Check whether authentication passes for a list of maintainers.

        Returns True if at least one of the mntners in mntner_list
        passes authentication, given self.passwords and
        self.keycert_obj_pk. Updates and checks self.passed_mntner_cache
        to prevent double checking of maintainers.
        """
        mntner_pk_set = set(mntner_pk_list)
        mntner_objs: List[RPSLMntner] = [m for m in self._mntner_db_cache if m.pk() in mntner_pk_set and m.source() == source]
        mntner_pks_to_resolve: Set[str] = mntner_pk_set - {m.pk() for m in mntner_objs}

        if mntner_pks_to_resolve:
            query = RPSLDatabaseQuery().sources([source])
            query = query.object_classes(['mntner']).rpsl_pks(mntner_pks_to_resolve)
            results = list(self.database_handler.execute_query(query))

            retrieved_mntner_objs: List[RPSLMntner] = [rpsl_object_from_text(r['object_text']) for r in results]   # type: ignore
            self._mntner_db_cache.update(retrieved_mntner_objs)
            mntner_objs += retrieved_mntner_objs

        for mntner_name in mntner_pk_list:
            if mntner_name in self._pre_approved:
                return True, mntner_objs

        for mntner_obj in mntner_objs:
            if mntner_obj.verify_auth(self.passwords, self.keycert_obj_pk):
                return True, mntner_objs

        return False, mntner_objs
예제 #5
0
 def _prepare_query(self,
                    column_names=None,
                    ordered_by_sources=True) -> RPSLDatabaseQuery:
     """Prepare an RPSLDatabaseQuery by applying relevant sources/class filters."""
     query = RPSLDatabaseQuery(column_names, ordered_by_sources)
     if self.sources and self.sources != self.all_valid_sources:
         query.sources(self.sources)
     else:
         default = list(get_setting('sources_default', []))
         if default:
             query.sources(list(default))
     if self.object_classes:
         query.object_classes(self.object_classes)
     if self.rpki_invalid_filter_enabled:
         query.rpki_status([RPKIStatus.not_found, RPKIStatus.valid])
     return query
예제 #6
0
파일: parser.py 프로젝트: icing/irrd
    def _retrieve_existing_version(self):
        """
        Retrieve the current version of this object, if any, and store it in rpsl_obj_current.
        Update self.status appropriately.
        """
        query = RPSLDatabaseQuery().sources([self.rpsl_obj_new.source()])
        query = query.object_classes([self.rpsl_obj_new.rpsl_object_class
                                      ]).rpsl_pk(self.rpsl_obj_new.pk())
        results = list(self.database_handler.execute_query(query))

        if not results:
            self.request_type = UpdateRequestType.CREATE
            logger.debug(
                f'{id(self)}: Did not find existing version for object {self.rpsl_obj_new}, request is CREATE'
            )
        elif len(results) == 1:
            self.request_type = UpdateRequestType.MODIFY
            self.rpsl_obj_current = rpsl_object_from_text(
                results[0]['object_text'], strict_validation=False)
            logger.debug(f'{id(self)}: Retrieved existing version for object '
                         f'{self.rpsl_obj_current}, request is MODIFY/DELETE')
        else:  # pragma: no cover
            # This should not be possible, as rpsl_pk/source are a composite unique value in the database scheme.
            # Therefore, a query should not be able to affect more than one row.
            affected_pks = ', '.join([r['pk'] for r in results])
            msg = f'{id(self)}: Attempted to retrieve current version of object {self.rpsl_obj_new.pk()}/'
            msg += f'{self.rpsl_obj_new.source()}, but multiple '
            msg += f'objects were found, internal pks found: {affected_pks}'
            logger.error(msg)
            raise ValueError(msg)
예제 #7
0
def _resolve_subquery(rpsl_object,
                      info: GraphQLResolveInfo,
                      object_classes: List[str],
                      pk_field: str,
                      sticky_source=True):
    """
    Resolve a subquery, like techCobjs, on an RPSL object, considering
    a number of object classes, extracting the PK from pk_field.
    If sticky_source is set, the referred object must be from the same source.
    """
    pks = rpsl_object.get(pk_field)
    if not pks:
        return []
    if not isinstance(pks, list):
        pks = [pks]
    query = RPSLDatabaseQuery(
        column_names=_columns_for_graphql_selection(info),
        ordered_by_sources=False,
        enable_ordering=False)
    query.object_classes(object_classes).rpsl_pks(pks)
    if sticky_source:
        query.sources([rpsl_object['source']])
    return _rpsl_db_query_to_graphql_out(query, info)
예제 #8
0
    def validate_all_rpsl_objects(self, database_handler: DatabaseHandler) -> \
            Tuple[List[Dict[str, str]], List[Dict[str, str]], List[Dict[str, str]]]:
        """
        Apply the scope filter to all relevant objects.

        Retrieves all routes from the DB, and aggregates the validation results.
        Returns a tuple of three sets:
        - one with routes that should be set to status in_scope, but are not now
        - one with routes that should be set to status out_scope_as, but are not now
        - one with routes that should be set to status out_scope_prefix, but are not now
        Each object is recorded as a dict, which has the fields shown
        in "columns" below.

        Objects where their current status in the DB matches the new
        validation result, are not included in the return value.
        """
        columns = [
            'rpsl_pk', 'ip_first', 'prefix_length', 'asn_first', 'source',
            'object_class', 'object_text', 'scopefilter_status'
        ]

        objs_changed: Dict[ScopeFilterStatus,
                           List[Dict[str, str]]] = defaultdict(list)

        q = RPSLDatabaseQuery(column_names=columns, enable_ordering=False)
        q = q.object_classes(['route', 'route6'])
        results = database_handler.execute_query(q)

        for result in results:
            current_status = result['scopefilter_status']
            result['old_status'] = current_status
            prefix = None
            if result['ip_first']:
                prefix = IP(result['ip_first'] + '/' +
                            str(result['prefix_length']))
            new_status, _ = self._validate_rpsl_data(
                result['source'],
                result['object_class'],
                prefix,
                result['asn_first'],
            )
            if new_status != current_status:
                result['scopefilter_status'] = new_status
                objs_changed[new_status].append(result)
        return (objs_changed[ScopeFilterStatus.in_scope],
                objs_changed[ScopeFilterStatus.out_scope_as],
                objs_changed[ScopeFilterStatus.out_scope_prefix])
예제 #9
0
    def validate_all_routes(self, sources: List[str]=None) -> \
            Tuple[List[Dict[str, str]], List[Dict[str, str]], List[Dict[str, str]]]:
        """
        Validate all RPSL route/route6 objects.

        Retrieves all routes from the DB, and aggregates the validation results.
        Returns a tuple of three sets of RPSL route(6)'s:
        - one with routes that should be set to status VALID, but are not now
        - one with routes that should be set to status INVALID, but are not now
        - one with routes that should be set to status UNKNOWN, but are not now
        Each route is recorded as a dict, which has the fields shown
        in "columns" below.

        Routes where their current validation status in the DB matches the new
        validation result, are not included in the return value.
        """
        columns = [
            'rpsl_pk', 'ip_first', 'prefix_length', 'asn_first', 'source',
            'object_class', 'object_text', 'rpki_status'
        ]
        q = RPSLDatabaseQuery(column_names=columns, enable_ordering=False)
        q = q.object_classes(['route', 'route6'])
        if sources:
            q = q.sources(sources)
        routes = self.database_handler.execute_query(q)

        objs_changed: Dict[RPKIStatus, List[Dict[str,
                                                 str]]] = defaultdict(list)

        for result in routes:
            # RPKI_IRR_PSEUDO_SOURCE objects are ROAs, and don't need validation.
            if result['source'] == RPKI_IRR_PSEUDO_SOURCE:
                continue

            current_status = result['rpki_status']
            result['old_status'] = current_status
            new_status = self.validate_route(result['ip_first'],
                                             result['prefix_length'],
                                             result['asn_first'],
                                             result['source'])
            if new_status != current_status:
                result['rpki_status'] = new_status
                objs_changed[new_status].append(result)

        return objs_changed[RPKIStatus.valid], objs_changed[
            RPKIStatus.invalid], objs_changed[RPKIStatus.not_found]
예제 #10
0
def resolve_rpsl_objects(_, info: GraphQLResolveInfo, **kwargs):
    """
    Resolve a `rpslObjects` query. This query has a considerable
    number of parameters, each of which is applied to an RPSL
    database query.
    """
    low_specificity_kwargs = {
        'object_class', 'rpki_status', 'scope_filter_status', 'sources',
        'sql_trace'
    }
    # A query is sufficiently specific if it has other fields than listed above,
    # except that rpki_status is sufficient if it is exclusively selecting on
    # valid or invalid.
    low_specificity = all([
        not (set(kwargs.keys()) - low_specificity_kwargs),
        kwargs.get('rpki_status', []) not in [[RPKIStatus.valid],
                                              [RPKIStatus.invalid]],
    ])
    if low_specificity:
        raise ValueError('Your query must be more specific.')

    if kwargs.get('sql_trace'):
        info.context['sql_trace'] = True

    query = RPSLDatabaseQuery(
        column_names=_columns_for_graphql_selection(info),
        ordered_by_sources=False,
        enable_ordering=False)

    if 'record_limit' in kwargs:
        query.limit(kwargs['record_limit'])
    if 'rpsl_pk' in kwargs:
        query.rpsl_pks(kwargs['rpsl_pk'])
    if 'object_class' in kwargs:
        query.object_classes(kwargs['object_class'])
    if 'asn' in kwargs:
        query.asns_first(kwargs['asn'])
    if 'text_search' in kwargs:
        query.text_search(kwargs['text_search'])
    if 'rpki_status' in kwargs:
        query.rpki_status(kwargs['rpki_status'])
    else:
        query.rpki_status([RPKIStatus.not_found, RPKIStatus.valid])
    if 'scope_filter_status' in kwargs:
        query.scopefilter_status(kwargs['scope_filter_status'])
    else:
        query.scopefilter_status([ScopeFilterStatus.in_scope])

    all_valid_sources = set(get_setting('sources', {}).keys())
    if get_setting('rpki.roa_source'):
        all_valid_sources.add(RPKI_IRR_PSEUDO_SOURCE)
    sources_default = set(get_setting('sources_default', []))

    if 'sources' in kwargs:
        query.sources(kwargs['sources'])
    elif sources_default and sources_default != all_valid_sources:
        query.sources(list(sources_default))

    # All other parameters are generic lookup fields, like `members`
    for attr, value in kwargs.items():
        attr = attr.replace('_', '-')
        if attr in lookup_fields:
            query.lookup_attrs_in([attr], value)

    ip_filters = [
        'ip_exact', 'ip_less_specific', 'ip_more_specific',
        'ip_less_specific_one_level', 'ip_any'
    ]
    for ip_filter in ip_filters:
        if ip_filter in kwargs:
            getattr(query, ip_filter)(IP(kwargs[ip_filter]))

    return _rpsl_db_query_to_graphql_out(query, info)
예제 #11
0
파일: validators.py 프로젝트: irrdnet/irrd
    def validate_all_rpsl_objects(self, database_handler: DatabaseHandler) -> \
            Tuple[List[Dict[str, str]], List[Dict[str, str]], List[Dict[str, str]]]:
        """
        Apply the scope filter to all relevant objects.

        Retrieves all routes from the DB, and aggregates the validation results.
        Returns a tuple of three sets:
        - one with routes that should be set to status in_scope, but are not now
        - one with routes that should be set to status out_scope_as, but are not now
        - one with routes that should be set to status out_scope_prefix, but are not now
        Each object is recorded as a dict, which has the fields shown
        in "columns" below.

        Objects where their current status in the DB matches the new
        validation result, are not included in the return value.
        """
        columns = [
            'pk', 'rpsl_pk', 'prefix', 'asn_first', 'source', 'object_class',
            'scopefilter_status', 'rpki_status'
        ]

        objs_changed: Dict[ScopeFilterStatus,
                           List[Dict[str, str]]] = defaultdict(list)

        q = RPSLDatabaseQuery(column_names=columns, enable_ordering=False)
        q = q.object_classes(['route', 'route6', 'aut-num'])
        results = database_handler.execute_query(q)

        for result in results:
            current_status = result['scopefilter_status']
            result['old_status'] = current_status
            prefix = None
            if result.get('prefix'):
                prefix = IP(result['prefix'])
            new_status, _ = self._validate_rpsl_data(
                result['source'],
                result['object_class'],
                prefix,
                result['asn_first'],
            )
            if new_status != current_status:
                result['scopefilter_status'] = new_status
                objs_changed[new_status].append(result)

        # Object text is only retrieved for objects with state changes
        pks_to_enrich = [
            obj['pk'] for objs in objs_changed.values() for obj in objs
        ]
        query = RPSLDatabaseQuery(['pk', 'object_text'],
                                  enable_ordering=False).pks(pks_to_enrich)
        rows_per_pk = {
            row['pk']: row
            for row in database_handler.execute_query(query)
        }

        for rpsl_objs in objs_changed.values():
            for rpsl_obj in rpsl_objs:
                rpsl_obj.update(rows_per_pk[rpsl_obj['pk']])

        return (objs_changed[ScopeFilterStatus.in_scope],
                objs_changed[ScopeFilterStatus.out_scope_as],
                objs_changed[ScopeFilterStatus.out_scope_prefix])
예제 #12
0
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()
예제 #13
0
 def init_query(rpsl_object_class: str) -> RPSLDatabaseQuery:
     query = RPSLDatabaseQuery().sources([rpsl_obj_new.source()])
     query = query.object_classes([rpsl_object_class])
     return query.first_only()