From 0be93ec05b04117c7b8f7cfe641d1abd64ccd50b Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Thu, 2 May 2019 10:01:02 +0200
Subject: [PATCH] Fix #563: unplayable radios for anonymous users

---
 api/config/settings/common.py           |  2 +-
 api/funkwhale_api/common/permissions.py |  2 +-
 api/funkwhale_api/radios/radios.py      |  8 ++++--
 api/funkwhale_api/radios/serializers.py |  2 +-
 api/funkwhale_api/radios/views.py       | 35 +++++++++++++++++++++----
 changes/changelog.d/563.bugfix          |  1 +
 front/src/components/library/Radios.vue |  4 +--
 front/src/store/auth.js                 |  4 ---
 8 files changed, 42 insertions(+), 16 deletions(-)
 create mode 100644 changes/changelog.d/563.bugfix

diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 13624511c2..3000feddba 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -536,11 +536,11 @@ REST_FRAMEWORK = {
     ),
     "DEFAULT_AUTHENTICATION_CLASSES": (
         "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
-        "rest_framework.authentication.SessionAuthentication",
         "funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS",
         "funkwhale_api.common.authentication.BearerTokenHeaderAuth",
         "funkwhale_api.common.authentication.JSONWebTokenAuthentication",
         "rest_framework.authentication.BasicAuthentication",
+        "rest_framework.authentication.SessionAuthentication",
     ),
     "DEFAULT_PERMISSION_CLASSES": (
         "funkwhale_api.users.oauth.permissions.ScopePermission",
diff --git a/api/funkwhale_api/common/permissions.py b/api/funkwhale_api/common/permissions.py
index 4ab405eb47..237fc4ae41 100644
--- a/api/funkwhale_api/common/permissions.py
+++ b/api/funkwhale_api/common/permissions.py
@@ -47,6 +47,6 @@ class OwnerPermission(BasePermission):
 
         owner_field = getattr(view, "owner_field", "user")
         owner = operator.attrgetter(owner_field)(obj)
-        if owner != request.user:
+        if not owner or not request.user.is_authenticated or owner != request.user:
             raise Http404
         return True
diff --git a/api/funkwhale_api/radios/radios.py b/api/funkwhale_api/radios/radios.py
index f19c6b884d..a0dc36b62a 100644
--- a/api/funkwhale_api/radios/radios.py
+++ b/api/funkwhale_api/radios/radios.py
@@ -47,6 +47,8 @@ class SessionRadio(SimpleRadio):
         qs = Track.objects.all()
         if not self.session:
             return qs
+        if not self.session.user:
+            return qs
         query = moderation_filters.get_filtered_content_query(
             config=moderation_filters.USER_FILTER_CONFIG["TRACK"],
             user=self.session.user,
@@ -62,7 +64,9 @@ class SessionRadio(SimpleRadio):
         if self.session:
             queryset = self.filter_from_session(queryset)
             if kwargs.pop("filter_playable", True):
-                queryset = queryset.playable_by(self.session.user.actor)
+                queryset = queryset.playable_by(
+                    self.session.user.actor if self.session.user else None
+                )
         queryset = self.filter_queryset(queryset)
         return queryset
 
@@ -129,7 +133,7 @@ class CustomRadio(SessionRadio):
         try:
             user = data["user"]
         except KeyError:
-            user = context["user"]
+            user = context.get("user")
         try:
             assert data["custom_radio"].user == user or data["custom_radio"].is_public
         except KeyError:
diff --git a/api/funkwhale_api/radios/serializers.py b/api/funkwhale_api/radios/serializers.py
index 9bffbf5b9c..397452ecc0 100644
--- a/api/funkwhale_api/radios/serializers.py
+++ b/api/funkwhale_api/radios/serializers.py
@@ -70,7 +70,7 @@ class RadioSessionSerializer(serializers.ModelSerializer):
         return data
 
     def create(self, validated_data):
-        validated_data["user"] = self.context["user"]
+        validated_data["user"] = self.context.get("user")
         if validated_data.get("related_object_id"):
             radio = registry[validated_data["radio_type"]]()
             validated_data["related_object"] = radio.get_related_object(
diff --git a/api/funkwhale_api/radios/views.py b/api/funkwhale_api/radios/views.py
index fa0a6fe4a6..0a5f1cd6cf 100644
--- a/api/funkwhale_api/radios/views.py
+++ b/api/funkwhale_api/radios/views.py
@@ -1,5 +1,5 @@
 from django.db.models import Q
-from rest_framework import mixins, permissions, status, viewsets
+from rest_framework import mixins, status, viewsets
 from rest_framework.decorators import action
 from rest_framework.response import Response
 
@@ -28,6 +28,7 @@ class RadioViewSet(
     required_scope = "radios"
     owner_field = "user"
     owner_checks = ["write"]
+    anonymous_policy = "setting"
 
     def get_queryset(self):
         queryset = models.Radio.objects.all()
@@ -82,11 +83,30 @@ class RadioSessionViewSet(
 
     serializer_class = serializers.RadioSessionSerializer
     queryset = models.RadioSession.objects.all()
-    permission_classes = [permissions.IsAuthenticated]
+    permission_classes = []
 
     def get_queryset(self):
         queryset = super().get_queryset()
-        return queryset.filter(user=self.request.user)
+        if self.request.user.is_authenticated:
+            return queryset.filter(
+                Q(user=self.request.user)
+                | Q(session_key=self.request.session.session_key)
+            )
+
+        return queryset.filter(session_key=self.request.session.session_key).exclude(
+            session_key=None
+        )
+
+    def perform_create(self, serializer):
+        if (
+            not self.request.user.is_authenticated
+            and not self.request.session.session_key
+        ):
+            self.request.session.create()
+        return serializer.save(
+            user=self.request.user if self.request.user.is_authenticated else None,
+            session_key=self.request.session.session_key,
+        )
 
     def get_serializer_context(self):
         context = super().get_serializer_context()
@@ -97,14 +117,19 @@ class RadioSessionViewSet(
 class RadioSessionTrackViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
     serializer_class = serializers.RadioSessionTrackSerializer
     queryset = models.RadioSessionTrack.objects.all()
-    permission_classes = [permissions.IsAuthenticated]
+    permission_classes = []
 
     def create(self, request, *args, **kwargs):
         serializer = self.get_serializer(data=request.data)
         serializer.is_valid(raise_exception=True)
         session = serializer.validated_data["session"]
+        if not request.user.is_authenticated and not request.session.session_key:
+            self.request.session.create()
         try:
-            assert request.user == session.user
+            assert (request.user == session.user) or (
+                request.session.session_key == session.session_key
+                and session.session_key
+            )
         except AssertionError:
             return Response(status=status.HTTP_403_FORBIDDEN)
         session.radio.pick()
diff --git a/changes/changelog.d/563.bugfix b/changes/changelog.d/563.bugfix
new file mode 100644
index 0000000000..14f49b75e7
--- /dev/null
+++ b/changes/changelog.d/563.bugfix
@@ -0,0 +1 @@
+Fixed unplayable radios for anonymous users (#563)
diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue
index 68eb11f0b1..9b2f7bf820 100644
--- a/front/src/components/library/Radios.vue
+++ b/front/src/components/library/Radios.vue
@@ -10,9 +10,9 @@
           <translate translate-context="Content/Radio/Title">Instance radios</translate>
         </h3>
         <div class="ui cards">
-          <radio-card :type="'favorites'"></radio-card>
+          <radio-card v-if="$store.state.auth.authenticated" :type="'favorites'"></radio-card>
           <radio-card :type="'random'"></radio-card>
-          <radio-card :type="'less-listened'"></radio-card>
+          <radio-card v-if="$store.state.auth.authenticated" :type="'less-listened'"></radio-card>
         </div>
       </div>
 
diff --git a/front/src/store/auth.js b/front/src/store/auth.js
index 29c4c5d719..52cc98a6de 100644
--- a/front/src/store/auth.js
+++ b/front/src/store/auth.js
@@ -118,10 +118,6 @@ export default {
       }
     },
     fetchProfile ({commit, dispatch, state}) {
-      if (document) {
-        // this is to ensure we do not have any leaking cookie set by django
-        document.cookie = 'sessionid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'
-      }
 
       return new Promise((resolve, reject) => {
         axios.get('users/users/me/').then((response) => {
-- 
GitLab