Example #1
0
    def __init__(self,
                 source,
                 attribute=None,
                 empty_value=None,
                 func=None,
                 help_text=None,
                 html_extra_classes=None,
                 include_label=False,
                 is_attribute_absolute_url=False,
                 is_object_absolute_url=False,
                 is_identifier=False,
                 is_sortable=False,
                 kwargs=None,
                 label=None,
                 order=None,
                 sort_field=None,
                 views=None,
                 widget=None,
                 widget_condition=None):
        self._label = label
        self._help_text = help_text
        self.source = source
        self.attribute = attribute
        self.empty_value = empty_value
        self.exclude = ()
        self.func = func
        self.html_extra_classes = html_extra_classes
        self.is_attribute_absolute_url = is_attribute_absolute_url
        self.is_object_absolute_url = is_object_absolute_url
        self.is_identifier = is_identifier
        self.is_sortable = is_sortable
        self.kwargs = kwargs or {}
        self.include_label = include_label
        self.order = order or 0
        self.sort_field = sort_field
        self.views = views or []
        self.widget = widget
        self.widget_condition = widget_condition

        if self.is_attribute_absolute_url or self.is_object_absolute_url:
            if not self.widget:
                self.widget = SourceColumnLinkWidget

        self.__class__._registry.setdefault(source, [])
        self.__class__._registry[source].append(self)

        self._calculate_label()
        self._calculate_help_text()
        if self.is_sortable:
            field_name = self.sort_field or self.attribute
            try:
                get_related_field(model=self.source,
                                  related_field_name=field_name)
            except FieldDoesNotExist as exception:
                raise ImproperlyConfigured(
                    '"{}" is not a field of "{}", cannot be used as a '
                    'sortable column.'.format(field_name,
                                              self.source)) from exception
Example #2
0
    def register_inheritance(cls, model, related):
        model_reverse = get_related_field(
            model=model, related_field_name=related).related_model
        cls._inheritances_reverse.setdefault(model_reverse, [])
        cls._inheritances_reverse[model_reverse].append(model)

        cls._inheritances[model] = related
Example #3
0
    def _initialize(self):
        for search_field in self.search_fields:
            related_model = get_related_field(
                model=self.model, related_field_name=search_field.field).model

            if related_model != self.model:
                self.__class__._model_search_relationships.setdefault(
                    self.model, set())
                self.__class__._model_search_relationships[self.model].add(
                    related_model)

                self.__class__._model_search_relationships.setdefault(
                    related_model, set())
                self.__class__._model_search_relationships[related_model].add(
                    self.model)
Example #4
0
    def _get_acl_filters(self,
                         queryset,
                         stored_permission,
                         user,
                         related_field_name=None):
        """
        This method does the bulk of the work. It generates filters for the
        AccessControlList model to determine if there are ACL entries for the
        members of the queryset's model provided.
        """
        # Determine which of the cases we need to address
        # 1: No related field
        # 2: Related field
        # 3: Related field that is Generic Foreign Key
        # 4: No related field, but has an inherited related field, solved by
        # recursion, branches to #2 or #3.
        # 5: Inherited field of a related field
        # 6: Inherited field of a related field that is Generic Foreign Key
        # -- Not addressed yet --
        # 7: Has a related function
        result = []

        if related_field_name:
            related_field = get_related_field(
                model=queryset.model, related_field_name=related_field_name)

            if isinstance(related_field, GenericForeignKey):
                # Case 3: Generic Foreign Key, multiple ContentTypes + object
                # id combinations
                # Also handles case #6 using the parent related field
                # reference template.

                # Craft a double underscore reference to a previous related
                # field in the case where multiple related fields are
                # associated.
                # Example: object_layer__content_type
                recuisive_related_reference = '__'.join(
                    related_field_name.split('__')[0:-1])

                # If there is at least one parent related field we add a
                # double underscore to make it a valid filter template.
                if recuisive_related_reference:
                    recuisive_related_reference = '{}__'.format(
                        recuisive_related_reference)

                content_type_object_id_queryset = queryset.annotate(
                    ct_fk_combination=Concat(
                        '{}{}'.format(recuisive_related_reference,
                                      related_field.ct_field),
                        Value('-'),
                        '{}{}'.format(recuisive_related_reference,
                                      related_field.fk_field),
                        output_field=CharField())).values('ct_fk_combination')

                acl_filter = self.annotate(ct_fk_combination=Concat(
                    'content_type',
                    Value('-'),
                    'object_id',
                    output_field=CharField())).filter(
                        permissions=stored_permission,
                        role__groups__user=user,
                        ct_fk_combination__in=content_type_object_id_queryset
                    ).values('object_id')

                field_lookup = '{}object_id__in'.format(
                    recuisive_related_reference)
                result.append(Q(**{field_lookup: acl_filter}))
            else:
                # Case 2: Related field of a single type, single ContentType,
                # multiple object id
                content_type = ContentType.objects.get_for_model(
                    model=related_field.related_model)
                field_lookup = '{}_id__in'.format(related_field_name)
                acl_filter = self.filter(
                    content_type=content_type,
                    permissions=stored_permission,
                    role__groups__user=user).values('object_id')
                # Don't add empty filters otherwise the default AND operator
                # of the Q object will return an empty queryset when reduced
                # and filter out objects that should be in the final queryset.
                if acl_filter:
                    result.append(Q(**{field_lookup: acl_filter}))

                # Case 5: Related field, has an inherited related field itself
                # Bubble up permssion check
                # Recurse and reduce
                # TODO: Add relationship support: OR or AND
                # TODO: OR for document pages, version, doc, and types
                # TODO: AND for new cabinet levels ACLs
                try:
                    related_field_model_related_fields = (
                        ModelPermission.get_inheritance(
                            model=related_field.related_model), )
                except KeyError:
                    pass
                else:
                    relation_result = []
                    for related_field_model_related_field_name in related_field_model_related_fields:
                        related_field_name = '{}__{}'.format(
                            related_field_name,
                            related_field_model_related_field_name)
                        related_field_inherited_acl_queries = self._get_acl_filters(
                            queryset=queryset,
                            stored_permission=stored_permission,
                            user=user,
                            related_field_name=related_field_name)
                        if related_field_inherited_acl_queries:
                            relation_result.append(
                                reduce(operator.and_,
                                       related_field_inherited_acl_queries))

                    if relation_result:
                        result.append(reduce(operator.or_, relation_result))
        else:
            # Case 1: Original model, single ContentType, multiple object id
            content_type = ContentType.objects.get_for_model(
                model=queryset.model)
            field_lookup = 'id__in'
            acl_filter = self.filter(
                content_type=content_type,
                permissions=stored_permission,
                role__groups__user=user).values('object_id')
            result.append(Q(**{field_lookup: acl_filter}))

            # Case 4: Original model, has an inherited related field
            try:
                related_fields = (ModelPermission.get_inheritance(
                    model=queryset.model), )
            except KeyError:
                pass
            else:
                relation_result = []

                for related_field_name in related_fields:
                    inherited_acl_queries = self._get_acl_filters(
                        queryset=queryset,
                        stored_permission=stored_permission,
                        related_field_name=related_field_name,
                        user=user)
                    if inherited_acl_queries:
                        relation_result.append(
                            reduce(operator.and_, inherited_acl_queries))

                if relation_result:
                    result.append(reduce(operator.or_, relation_result))

            # Case 7: Has a function
            try:
                field_query_function = ModelPermission.get_field_query_function(
                    model=queryset.model)
            except KeyError:
                pass
            else:
                function_results = field_query_function()

                # Filter by the model's content type
                content_type = ContentType.objects.get_for_model(
                    model=queryset.model)
                acl_filter = self.filter(
                    content_type=content_type,
                    permissions=stored_permission,
                    role__groups__user=user).values('object_id')
                # Obtain an queryset of filtered, authorized model instances
                acl_queryset = queryset.model._meta.default_manager.filter(
                    id__in=acl_filter).filter(**function_results['acl_filter'])

                if 'acl_values' in function_results:
                    acl_queryset = acl_queryset.values(
                        *function_results['acl_values'])

                # Get the final query using the filtered queryset as the
                # reference
                result.append(
                    Q(**{function_results['field_lookup']: acl_queryset}))

        return result
Example #5
0
 def get_model_field(self):
     return get_related_field(model=self.get_model(),
                              related_field_name=self.field)