Browse Source

fix(bookmarks): strictly filter playlists by bookmarked ID and eliminate item-level leakage

- video: remove Q(playlist_items__video__id__in=...) to strictly filter by bookmarked playlist ID
- podcast: ensure playlist bookmark filter targets playlist IDs and accepts both PODCAST_PLAYLIST and PODCAST
- security/correctness: return empty queryset if is_bookmark is requested without authentication across video, podcast, library, and article views
master
Mohsen Taba 6 days ago
parent
commit
dd31a58013
  1. 4
      apps/article/views.py
  2. 4
      apps/library/views.py
  3. 12
      apps/podcast/views.py
  4. 34
      apps/video/views.py

4
apps/article/views.py

@ -204,7 +204,9 @@ class ArticleListAPIView(generics.ListAPIView):
# Filter by bookmarks if provided # Filter by bookmarks if provided
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark == 'true':
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return queryset.none()
# Import Bookmark model here to avoid circular imports # Import Bookmark model here to avoid circular imports
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark

4
apps/library/views.py

@ -158,7 +158,9 @@ class BookListView(ListAPIView):
# Filter by bookmarked books if requested # Filter by bookmarked books if requested
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark in ('true', '1') and self.request.user.is_authenticated:
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return queryset.none()
# Import Bookmark model here to avoid circular imports # Import Bookmark model here to avoid circular imports
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark

12
apps/podcast/views.py

@ -218,12 +218,14 @@ class PodcastListAPIView(generics.ListAPIView):
# Filter by bookmarks if provided # Filter by bookmarks if provided
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark == 'true':
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return queryset.none()
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark
bookmarked_ids = Bookmark.objects.filter( bookmarked_ids = Bookmark.objects.filter(
user=self.request.user, user=self.request.user,
service=Bookmark.ServiceChoices.PODCAST,
service__in=[Bookmark.ServiceChoices.PODCAST_PLAYLIST, Bookmark.ServiceChoices.PODCAST],
status=True status=True
).values_list('content_id', flat=True) ).values_list('content_id', flat=True)
@ -304,11 +306,13 @@ class UserPlaylistListAPIView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
# Filter by bookmarks if provided # Filter by bookmarks if provided
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark in ('true', '1') and self.request.user.is_authenticated:
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return Podcast.objects.none()
from apps.bookmark.models.bookmark import Bookmark from apps.bookmark.models.bookmark import Bookmark
bookmarked_ids = Bookmark.objects.filter( bookmarked_ids = Bookmark.objects.filter(
user=self.request.user, user=self.request.user,
service=Bookmark.ServiceChoices.PODCAST,
service__in=[Bookmark.ServiceChoices.PODCAST, Bookmark.ServiceChoices.PODCAST_PLAYLIST],
status=True status=True
).values_list('content_id', flat=True) ).values_list('content_id', flat=True)
return Podcast.objects.filter( return Podcast.objects.filter(

34
apps/video/views.py

@ -225,25 +225,18 @@ class VideoPlaylistListAPIView(generics.ListAPIView):
queryset = queryset.filter(collections__slug=collection_slug) queryset = queryset.filter(collections__slug=collection_slug)
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark in ('true', '1') and self.request.user.is_authenticated:
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return queryset.none()
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark
from django.db.models import Q
bookmarked_playlist_ids = Bookmark.objects.filter( bookmarked_playlist_ids = Bookmark.objects.filter(
user=self.request.user, user=self.request.user,
service=Bookmark.ServiceChoices.VIDEO_PLAYLIST,
service__in=[Bookmark.ServiceChoices.VIDEO_PLAYLIST, Bookmark.ServiceChoices.VIDEO],
status=True status=True
).values_list('content_id', flat=True) ).values_list('content_id', flat=True)
bookmarked_video_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.VIDEO,
status=True
).values_list('content_id', flat=True)
queryset = queryset.filter(
Q(id__in=bookmarked_playlist_ids) | Q(playlist_items__video__id__in=bookmarked_video_ids)
).distinct()
queryset = queryset.filter(id__in=bookmarked_playlist_ids)
sort = self.request.query_params.get('sort', '-created_at') sort = self.request.query_params.get('sort', '-created_at')
allowed_sorts = [ allowed_sorts = [
'created_at', '-created_at', 'view_count', '-view_count', 'created_at', '-created_at', 'view_count', '-view_count',
@ -343,25 +336,18 @@ class VideoListAPIView(generics.ListAPIView):
queryset = queryset.filter(collections__slug=collection_slug) queryset = queryset.filter(collections__slug=collection_slug)
is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower() is_bookmark = (self.request.query_params.get('is_bookmark') or self.request.query_params.get('is_bookmarked') or '').lower()
if is_bookmark in ('true', '1') and self.request.user.is_authenticated:
if is_bookmark in ('true', '1'):
if not self.request.user.is_authenticated:
return queryset.none()
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark
from django.db.models import Q
bookmarked_playlist_ids = Bookmark.objects.filter( bookmarked_playlist_ids = Bookmark.objects.filter(
user=self.request.user, user=self.request.user,
service=Bookmark.ServiceChoices.VIDEO_PLAYLIST,
service__in=[Bookmark.ServiceChoices.VIDEO_PLAYLIST, Bookmark.ServiceChoices.VIDEO],
status=True status=True
).values_list('content_id', flat=True) ).values_list('content_id', flat=True)
bookmarked_video_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.VIDEO,
status=True
).values_list('content_id', flat=True)
queryset = queryset.filter(
Q(id__in=bookmarked_playlist_ids) | Q(playlist_items__video__id__in=bookmarked_video_ids)
).distinct()
queryset = queryset.filter(id__in=bookmarked_playlist_ids)
sort = self.request.query_params.get('sort', '-created_at') sort = self.request.query_params.get('sort', '-created_at')
allowed_sorts = [ allowed_sorts = [
'created_at', '-created_at', 'view_count', '-view_count', 'created_at', '-created_at', 'view_count', '-view_count',

Loading…
Cancel
Save