def _aux_filter(cls, model: Type['CremeEntity'], sc_sequence: Sequence['SetCredentials'], user, queryset: QuerySet, perm: int) -> QuerySet: allowed_ctype_ids = {None, ContentType.objects.get_for_model(model).id} ESET_ALL = cls.ESET_ALL ESET_OWN = cls.ESET_OWN forbidden, allowed = split_filter( lambda sc: sc.forbidden, sorted( (sc for sc in sc_sequence if sc.ctype_id in allowed_ctype_ids and sc.value & perm), # NB: we sort to get ESET_ALL creds before ESET_OWN ones, then ESET_FILTER ones. key=lambda sc: sc.set_type, )) if not allowed: return queryset.none() if any(f.set_type == ESET_ALL for f in forbidden): return queryset.none() def user_filtering_kwargs(): # TODO: cache/lazy teams = user.teams return {'user__in': [user, *teams]} if teams else {'user': user} filtered_qs = queryset q = Q() for cred in allowed: set_type = cred.set_type if set_type == ESET_ALL: break if set_type == ESET_OWN: q |= Q(**user_filtering_kwargs()) else: # SetCredentials.ESET_FILTER # TODO: distinct ? (see EntityFilter.filter()) q |= cred.efilter.get_q(user=user) else: filtered_qs = filtered_qs.filter(q) for cred in forbidden: if cred.set_type == ESET_OWN: filtered_qs = filtered_qs.exclude(**user_filtering_kwargs()) else: # SetCredentials.ESET_FILTER filtered_qs = filtered_qs.exclude( cred.efilter.get_q(user=user)) return filtered_qs
def filter(self, _source: QuerySet, ids=None, **kwargs) -> QuerySet: if not self.Meta.is_public and not self.ctx.user.is_authenticated: _source = _source.none() if ids is not None: _source = _source.filter(id__in=ids) return _source
def retry_qs(qs: QuerySet, **kwargs): for delay in (0.5, 1, 2, 4, None): result = qs.filter(**kwargs) if result.exists(): return result else: if delay: time.sleep(delay) else: return qs.none()
def filter_queryset(self, request: Request, queryset: QuerySet, view: View) -> QuerySet: if request.user.role == RoleEnum.USER: return queryset.filter(id=request.user.id) elif request.user.role == RoleEnum.MANAGER: return queryset.filter(Q(role=int(RoleEnum.USER)) | Q(role=int(RoleEnum.MANAGER))) elif request.user.role == RoleEnum.ADMIN: return queryset else: self._logger.error(f'Unknown role {request.user.role}') return queryset.none()
def list_resolver(django_object_type, resolver, root, info, **args): queryset = maybe_queryset(resolver(root, info, **args)) if queryset is None: queryset = QuerySet.none() # FIXME: This will throw error if isinstance(queryset, QuerySet): if hasattr(django_object_type, 'get_queryset'): # Pass queryset to the DjangoObjectType get_queryset method queryset = maybe_queryset( django_object_type.get_queryset(queryset, info)) return queryset
def filter(self, user, queryset: QuerySet, perm: int) -> QuerySet: """Filter a QuerySet of CremeEntities by the credentials related to this role. Beware, the model class must be a child class of CremeEntity, but cannot be CremeEntity itself. @param user: A <django.contrib.auth.get_user_model()> instance (eg: CremeUser) ; should be related to the UserRole instance. @param queryset: A Queryset on a child class of CremeEntity. @param perm: A value in (EntityCredentials.VIEW, EntityCredentials.CHANGE etc...). @return: A new (filtered) queryset on the same model. """ model = queryset.model assert issubclass(model, CremeEntity) assert model is not CremeEntity if self.is_app_allowed_or_administrable(model._meta.app_label): queryset = SetCredentials.filter(self._get_setcredentials(), user, queryset, perm) else: queryset = queryset.none() return queryset
def search_escaped_and_unescaped(super_obj: admin.ModelAdmin, request, input_queryset: QuerySet, search_term: str): """ Can be called from the ``get_search_results()`` method of ``ModelAdmin`` classes, to search using both escaped and unescaped characters. For example, passing in "grøt" as ``search_term``, will search for both "grøt" and "grøt". """ # `use_distinct` starts as `False` in Django's `get_search_results()` combined_searched_querysets, use_distinct_result = input_queryset.none( ), False # Try both with and without escaping: for search_term_repr in (search_term, escape_to_named_characters(search_term)): searched_queryset, use_distinct = super_obj.get_search_results( request, input_queryset, search_term_repr) combined_searched_querysets = combined_searched_querysets.union( searched_queryset.order_by()) # clear ordering use_distinct_result |= use_distinct result_queryset = input_queryset.filter( pk__in={cb.pk for cb in combined_searched_querysets}) return result_queryset, use_distinct_result
def filter_node(self, queryset: QuerySet): is_query_all = self.get_query_param('all', True) node_id = self.get_query_param('node_id') node_name = self.get_query_param('node') if node_id: _nodes = Node.objects.filter(pk=node_id) elif node_name: _nodes = Node.objects.filter(value=node_name) else: return queryset if not _nodes: return queryset.none() node = _nodes.first() if not is_query_all: queryset = queryset.filter(nodes=node) return queryset nodeids = node.get_ancestors(with_self=True).values_list('id', flat=True) nodeids = list(nodeids) queryset = queryset.filter(nodes__in=nodeids) return queryset
def filter_queryset( self, queryset: QuerySet, feature_type: FeatureType ) -> QuerySet: """Apply the filters and lookups to the queryset. :param queryset: The queryset to filter. :param feature_type: The feature type that the queryset originated from. """ if self.is_empty: return queryset.none() if self.extra_lookups: # Each time an expression node calls add_extra_lookup(), # the parent should have used apply_extra_lookups() raise RuntimeError("apply_extra_lookups() was not called") # All are applied at once. if self.annotations: queryset = queryset.annotate(**self.annotations) lookups = self.lookups try: lookups += self.typed_lookups[feature_type.name] except KeyError: pass if lookups: queryset = queryset.filter(*lookups) if self.ordering: queryset = queryset.order_by(*self.ordering) if self.distinct: queryset = queryset.distinct() return queryset
def filter_entities(cls, sc_sequence: Sequence['SetCredentials'], user, queryset: QuerySet, perm: int, models: Iterable[Type['CremeEntity']], as_model=None) -> QuerySet: """Filter a queryset of entities with the given credentials. Beware, model class must be CremeEntity ; it cannot be a child class of CremeEntity. @param sc_sequence: A sequence of SetCredentials instances. @param user: A django.contrib.auth.get_user_model() instance (eg: CremeUser).e. @param queryset: Queryset with model=CremeEntity. @param perm: A value in (EntityCredentials.VIEW, EntityCredentials.CHANGE etc...). @param models: An iterable of CremeEntity-child-classes, corresponding to allowed models. @param as_model: A model inheriting CremeEntity, or None. If a model is given, all the entities in the queryset are filtered with the credentials for this model. BEWARE: you should probably use this feature only if the queryset if already filtered by its field 'entity_type' (to keep only entities of the right model, & so do not make mistakes with credentials). @return: A new queryset on CremeEntity. @raise: EntityCredentials.FilteringError if an EntityFilter which cannot be used on CremeEntity is found in <sc_sequence>. """ assert queryset.model is CremeEntity get_for_model = ContentType.objects.get_for_model def _check_efilters(sc_seq): if any(sc.efilter_id and not sc.efilter.applicable_on_entity_base for sc in sc_seq): raise EntityCredentials.FilteringError( "An EntityFilter (not targeting CremeEntity) is used by a " "{cls} instance so it's not possible to use " "{cls}.filter_entities().".format(cls=cls.__name__)) if as_model is not None: assert issubclass(as_model, CremeEntity) narrowed_ct_ids = {None, get_for_model(as_model).id} narrowed_sc = [ sc for sc in sc_sequence if sc.ctype_id in narrowed_ct_ids ] _check_efilters(narrowed_sc) return cls._aux_filter( model=as_model, sc_sequence=narrowed_sc, user=user, queryset=queryset, perm=perm, ) all_ct_ids = { None, *(get_for_model(model).id for model in models), } sorted_sc = sorted( (sc for sc in sc_sequence if sc.ctype_id in all_ct_ids), # NB: we sort to get ESET_ALL creds before ESET_OWN/ESET_FILTER ones. key=lambda sc: sc.set_type, ) _check_efilters(sorted_sc) # NB: some explanations on the algorithm : # we try to regroup ContentTypes (corresponding to CremeEntity sub_classes) # which have the same filtering rules ; so we can generate a Query which looks like # entity_type__in=[...] OR (entity_type__in=[...] AND user__exact=current-user) OR # (entity_type__in=[...] AND field1__startswith='foo') OWN_FILTER_ID = 0 # Fake EntityFilter ID corresponding to ESET_OWN. ESET_ALL = cls.ESET_ALL ESET_OWN = cls.ESET_OWN ESET_FILTER = cls.ESET_FILTER def _extract_filter_ids(set_creds): for sc in set_creds: if sc.set_type == ESET_OWN: yield OWN_FILTER_ID break # Avoid several OWN_FILTER_ID (should not happen) for sc in set_creds: if sc.set_type == ESET_FILTER: yield sc.efilter_id # Map of EntityFilters to apply on ContentTypes groups # key = tuple containing 2 tuples of filter IDs: forbidden rules & allowed ones. # value = list of ContentType IDs. # Note: special values for EntityFilter ID: # None: means ESET_ALL (no filtering) # OWN_FILTER_ID: means ESET_OWN (a virtual EntityFilter on "user" field). ctypes_filtering: DefaultDict[tuple, List[int]] = defaultdict(list) efilters_per_id = {sc.efilter_id: sc.efilter for sc in sc_sequence} for model in models: ct_id = get_for_model(model).id model_ct_ids = {None, ct_id} # <None> means <CremeEntity> forbidden, allowed = split_filter( lambda sc: sc.forbidden, (sc for sc in sorted_sc if sc.ctype_id in model_ct_ids and sc.value & perm)) if allowed: if forbidden and forbidden[0].set_type == ESET_ALL: continue allowed_filter_ids = [None] if allowed[0].set_type == ESET_ALL else \ [*_extract_filter_ids(allowed)] forbidden_filter_ids = [*_extract_filter_ids(forbidden)] ctypes_filtering[( tuple(forbidden_filter_ids), tuple(allowed_filter_ids), )].append(ct_id) if not ctypes_filtering: queryset = queryset.none() else: def _user_filtering_q(): # TODO: cached/lazy ? teams = user.teams return Q( ** {'user__in': [user, *teams]} if teams else {'user': user}) def _efilter_ids_to_Q(efilter_ids): filters_q = Q() for filter_id in efilter_ids: # TODO: condexpr if filter_id is not None: # None == ESET_ALL if filter_id == OWN_FILTER_ID: filter_q = _user_filtering_q() else: # TODO: distinct ?? filter_q = efilters_per_id[filter_id].get_q( user=user) filters_q |= filter_q return filters_q q = Q() for (forbidden_filter_ids, allowed_filter_ids), ct_ids in ctypes_filtering.items(): q |= ((Q(entity_type_id=ct_ids[0]) if len(ct_ids) == 1 else Q( entity_type_id__in=ct_ids)) & _efilter_ids_to_Q(allowed_filter_ids) & ~_efilter_ids_to_Q(forbidden_filter_ids)) queryset = queryset.filter(q) return queryset
def filter_queryset(self, request: Request, queryset: QuerySet, view: View) -> QuerySet: if request.user.is_authenticated: return queryset.filter(author=request.user) else: return queryset.none()