From 7f6464200352c1120a7c852c18cc629422f310c9 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Thu, 9 Apr 2026 19:58:40 -0500 Subject: [PATCH] security: Hardened the HLS proxy `change_stream` endpoint by converting it from a plain Django view to a DRF `@api_view` with `@permission_classes([IsAdmin])`, ensuring the endpoint actually enforces admin-only access. The previous decorator arrangement (`@csrf_exempt` + `@permission_classes`) had no effect on a plain Django view. --- apps/proxy/hls_proxy/views.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/proxy/hls_proxy/views.py b/apps/proxy/hls_proxy/views.py index eaf0f1cd..835b431e 100644 --- a/apps/proxy/hls_proxy/views.py +++ b/apps/proxy/hls_proxy/views.py @@ -4,6 +4,8 @@ import logging from django.http import StreamingHttpResponse, JsonResponse, HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods +from rest_framework.decorators import api_view, permission_classes +from apps.accounts.permissions import IsAdmin from .server import ProxyServer, Config logger = logging.getLogger(__name__) @@ -15,7 +17,7 @@ def stream_endpoint(request, channel_id): """Handle HLS manifest requests""" if channel_id not in proxy_server.stream_managers: return JsonResponse({'error': 'Channel not found'}, status=404) - + response = proxy_server.stream_endpoint(channel_id) return StreamingHttpResponse( response[0], @@ -30,10 +32,10 @@ def get_segment(request, segment_name): try: segment_num = int(segment_name.split('.')[0]) buffer = proxy_server.stream_buffers.get(segment_num) - + if not buffer: return JsonResponse({'error': 'Segment not found'}, status=404) - + return StreamingHttpResponse( buffer, content_type='video/MP2T' @@ -44,19 +46,19 @@ def get_segment(request, segment_name): logger.error(f"Error serving segment: {e}") return JsonResponse({'error': str(e)}, status=500) -@csrf_exempt -@require_http_methods(["POST"]) +@api_view(["POST"]) +@permission_classes([IsAdmin]) def change_stream(request, channel_id): """Change stream URL for existing channel""" try: if channel_id not in proxy_server.stream_managers: return JsonResponse({'error': 'Channel not found'}, status=404) - + data = json.loads(request.body) new_url = data.get('url') if not new_url: return JsonResponse({'error': 'No URL provided'}, status=400) - + manager = proxy_server.stream_managers[channel_id] if manager.update_url(new_url): return JsonResponse({ @@ -64,7 +66,7 @@ def change_stream(request, channel_id): 'channel': channel_id, 'url': new_url }) - + return JsonResponse({ 'message': 'URL unchanged', 'channel': channel_id, @@ -85,7 +87,7 @@ def initialize_stream(request, channel_id): url = data.get('url') if not url: return JsonResponse({'error': 'No URL provided'}, status=400) - + proxy_server.initialize_channel(url, channel_id) return JsonResponse({ 'message': 'Stream initialized',