Moves privacy filter to model class

This commit is contained in:
Mouse Reeve 2021-10-06 09:48:11 -07:00
parent 67c83b8d32
commit b2671e78ef
2 changed files with 72 additions and 53 deletions

View file

@ -2,8 +2,9 @@
import base64 import base64
from Crypto import Random from Crypto import Random
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied, FieldError
from django.db import models from django.db import models
from django.db.models import Q
from django.dispatch import receiver from django.dispatch import receiver
from django.http import Http404 from django.http import Http404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -105,6 +106,76 @@ class BookWyrmModel(models.Model):
raise PermissionDenied() raise PermissionDenied()
@classmethod
def privacy_filter(
cls, viewer, privacy_levels=None, following_only=False, **filters
):
"""filter objects that have "user" and "privacy" fields"""
queryset = cls.objects
if hasattr(queryset, "select_subclasses"):
queryset = queryset.select_subclasses()
privacy_levels = privacy_levels or ["public", "unlisted", "followers", "direct"]
# if there'd a deleted field, exclude deleted items
try:
queryset = queryset.filter(deleted=False)
except FieldError:
pass
# exclude blocks from both directions
if not viewer.is_anonymous:
queryset = queryset.exclude(
Q(user__blocked_by=viewer) | Q(user__blocks=viewer)
)
# you can't see followers only or direct messages if you're not logged in
if viewer.is_anonymous:
privacy_levels = [
p for p in privacy_levels if not p in ["followers", "direct"]
]
# filter to only privided privacy levels
queryset = queryset.filter(privacy__in=privacy_levels)
# only include statuses the user follows
if following_only:
queryset = queryset.exclude(
~Q( # remove everythign except
Q(user__followers=viewer)
| Q(user=viewer) # user following
| Q(mention_users=viewer) # is self # mentions user
),
)
# exclude followers-only statuses the user doesn't follow
elif "followers" in privacy_levels:
queryset = cls.followers_filter(queryset, viewer)
# exclude direct messages not intended for the user
if "direct" in privacy_levels:
queryset = cls.direct_filter(queryset, viewer)
return queryset
@classmethod
def followers_filter(cls, queryset, viewer):
"""Override-able filter for "followers" privacy level"""
return queryset.exclude(
~Q( # user isn't following and it isn't their own status
Q(user__followers=viewer) | Q(user=viewer)
),
privacy="followers", # and the status is followers only
)
@classmethod
def direct_filter(cls, queryset, viewer):
"""Override-able filter for "direct" privacy level"""
try:
return queryset.exclude(
~Q(Q(user=viewer) | Q(mention_users=viewer)), privacy="direct"
)
except FieldError:
return queryset.exclude(~Q(user=viewer), privacy="direct")
@receiver(models.signals.post_save) @receiver(models.signals.post_save)
# pylint: disable=unused-argument # pylint: disable=unused-argument

View file

@ -6,8 +6,6 @@ import dateutil.tz
from dateutil.parser import ParserError from dateutil.parser import ParserError
from requests import HTTPError from requests import HTTPError
from django.core.exceptions import FieldError
from django.db.models import Q
from django.http import Http404 from django.http import Http404
from bookwyrm import activitypub, models from bookwyrm import activitypub, models
@ -50,56 +48,6 @@ def is_bookwyrm_request(request):
return True return True
def privacy_filter(viewer, queryset, privacy_levels=None, following_only=False):
"""filter objects that have "user" and "privacy" fields"""
privacy_levels = privacy_levels or ["public", "unlisted", "followers", "direct"]
# if there'd a deleted field, exclude deleted items
try:
queryset = queryset.filter(deleted=False)
except FieldError:
pass
# exclude blocks from both directions
if not viewer.is_anonymous:
queryset = queryset.exclude(Q(user__blocked_by=viewer) | Q(user__blocks=viewer))
# you can't see followers only or direct messages if you're not logged in
if viewer.is_anonymous:
privacy_levels = [p for p in privacy_levels if not p in ["followers", "direct"]]
# filter to only privided privacy levels
queryset = queryset.filter(privacy__in=privacy_levels)
# only include statuses the user follows
if following_only:
queryset = queryset.exclude(
~Q( # remove everythign except
Q(user__followers=viewer)
| Q(user=viewer) # user following
| Q(mention_users=viewer) # is self # mentions user
),
)
# exclude followers-only statuses the user doesn't follow
elif "followers" in privacy_levels:
queryset = queryset.exclude(
~Q( # user isn't following and it isn't their own status
Q(user__followers=viewer) | Q(user=viewer)
),
privacy="followers", # and the status is followers only
)
# exclude direct messages not intended for the user
if "direct" in privacy_levels:
try:
queryset = queryset.exclude(
~Q(Q(user=viewer) | Q(mention_users=viewer)), privacy="direct"
)
except FieldError:
queryset = queryset.exclude(~Q(user=viewer), privacy="direct")
return queryset
def handle_remote_webfinger(query): def handle_remote_webfinger(query):
"""webfingerin' other servers""" """webfingerin' other servers"""
user = None user = None