diff --git a/portier/settings.py b/portier/settings.py index 3d304b4..c4052f7 100644 --- a/portier/settings.py +++ b/portier/settings.py @@ -40,6 +40,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', 'guardian', 'django_registration', 'bootstrap4', @@ -155,6 +156,15 @@ STATICFILES_DIRS = [ ] +# DRF + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.DjangoObjectPermissions' + ] +} + + # E-mail DEFAULT_FROM_EMAIL = os.environ.get('EMAIL_FROM') EMAIL_HOST = os.environ.get('EMAIL_HOST') diff --git a/portier/urls.py b/portier/urls.py index 98d03f3..8c6a2ba 100644 --- a/portier/urls.py +++ b/portier/urls.py @@ -24,5 +24,6 @@ urlpatterns = [ path('rtmp/', include('rtmp.urls')), path('restream/', include('restream.urls')), path('concierge/', include('concierge.urls')), + path('api/v1/', include('restapi.urls')), path('', include('portal.urls')), ] diff --git a/requirements.txt b/requirements.txt index 7b5836e..7a5e02d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,6 @@ django-fontawesome-5 celery>=4.4 gunicorn>=20 psycopg2-binary +djangorestframework +django-filter +djangorestframework-guardian diff --git a/restapi/__init__.py b/restapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/restapi/apps.py b/restapi/apps.py new file mode 100644 index 0000000..a0a4de1 --- /dev/null +++ b/restapi/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class RestapiConfig(AppConfig): + name = 'restapi' diff --git a/restapi/migrations/__init__.py b/restapi/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/restapi/urls.py b/restapi/urls.py new file mode 100644 index 0000000..6b777b0 --- /dev/null +++ b/restapi/urls.py @@ -0,0 +1,17 @@ +from django.urls import path, include +from rest_framework import routers + +from .views import ApplicationViewSet, StreamViewSet, RestreamConfigViewSet + + +router = routers.DefaultRouter() +router.register(r'applications', ApplicationViewSet) +router.register(r'streams', StreamViewSet) +router.register(r'restreamconfigs', RestreamConfigViewSet) + +app_name = 'restapi' + +urlpatterns = [ + path('', include(router.urls)), + path('auth/', include('rest_framework.urls', namespace='rest_framework')) +] diff --git a/restapi/views.py b/restapi/views.py new file mode 100644 index 0000000..d13dcfd --- /dev/null +++ b/restapi/views.py @@ -0,0 +1,63 @@ +from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin +from rest_framework import serializers, viewsets +from rest_framework_guardian import filters +from rtmp.models import Application, Stream +from restream.models import RestreamConfig + + +class ApplicationSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Application + fields = ['id', 'name'] + + +class ApplicationViewSet(viewsets.ReadOnlyModelViewSet): + queryset = Application.objects.all() + serializer_class = ApplicationSerializer + filter_backends = [filters.ObjectPermissionsFilter] + + +class StreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer): + class Meta: + model = Stream + fields = ['id', 'stream', 'name', 'application'] + + def get_permissions_map(self, created): + current_user = self.context['request'].user + return { + 'view_stream': [current_user], + 'change_stream': [current_user], + 'delete_stream': [current_user] + } + + +class StreamViewSet(viewsets.ModelViewSet): + queryset = Stream.objects.all() + serializer_class = StreamSerializer + filter_backends = [filters.ObjectPermissionsFilter] + + +class RestreamConfigSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer): + class Meta: + model = RestreamConfig + fields = '__all__' + + def get_permissions_map(self, created): + current_user = self.context['request'].user + return { + 'view_restreamconfig': [current_user], + 'change_restreamconfig': [current_user], + 'delete_restreamconfig': [current_user] + } + + def validate_stream(self, value): + request = self.context['request'] + if not request.user.has_perm('rtmp.view_stream', value): + raise serializers.ValidationError('Access to stream is not authorized') + return value + + +class RestreamConfigViewSet(viewsets.ModelViewSet): + queryset = RestreamConfig.objects.all() + serializer_class = RestreamConfigSerializer + filter_backends = [filters.ObjectPermissionsFilter]