add object permissions with guardian; users only have access to their own streams/restreamconfigs
This commit is contained in:
		
							parent
							
								
									b16e9c955c
								
							
						
					
					
						commit
						730889b9a1
					
				| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from guardian.models import UserObjectPermission
 | 
			
		||||
from guardian.models import GroupObjectPermission
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def remove_obj_perms_connected_with_user(sender, instance, **kwargs):
 | 
			
		||||
    filters = Q(content_type=ContentType.objects.get_for_model(instance), object_pk=instance.pk)
 | 
			
		||||
    UserObjectPermission.objects.filter(filters).delete()
 | 
			
		||||
    GroupObjectPermission.objects.filter(filters).delete()
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +36,7 @@ INSTALLED_APPS = [
 | 
			
		|||
    'django.contrib.sessions',
 | 
			
		||||
    'django.contrib.messages',
 | 
			
		||||
    'django.contrib.staticfiles',
 | 
			
		||||
    'guardian',
 | 
			
		||||
    'django_registration',
 | 
			
		||||
    'bootstrap4',
 | 
			
		||||
    'fa',
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +56,11 @@ MIDDLEWARE = [
 | 
			
		|||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
AUTHENTICATION_BACKENDS = (
 | 
			
		||||
    'django.contrib.auth.backends.ModelBackend',
 | 
			
		||||
    'guardian.backends.ObjectPermissionBackend',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
ROOT_URLCONF = 'portier.urls'
 | 
			
		||||
 | 
			
		||||
TEMPLATES = [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
django>=3.0
 | 
			
		||||
django-registration>=3.1
 | 
			
		||||
django-bootstrap4
 | 
			
		||||
django-guardian
 | 
			
		||||
django-fa
 | 
			
		||||
celery>=4.4
 | 
			
		||||
gunicorn>=20
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
from django.contrib import admin
 | 
			
		||||
from guardian.admin import GuardedModelAdmin
 | 
			
		||||
from .models import RestreamConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamConfigAdmin(admin.ModelAdmin):
 | 
			
		||||
class RestreamConfigAdmin(GuardedModelAdmin):
 | 
			
		||||
    fields = ['name', 'active', 'stream', 'target']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
from django.forms import ModelForm
 | 
			
		||||
from guardian.shortcuts import get_objects_for_user
 | 
			
		||||
from . import models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamConfigFilteredStreamForm(ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = models.RestreamConfig
 | 
			
		||||
        fields = ['name', 'stream', 'target', 'active']
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        user = kwargs.pop('user', None)
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # limit the stream selection to user-accessible streams
 | 
			
		||||
        self.fields['stream'].queryset = get_objects_for_user(user, 'rtmp.view_stream')
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.db.models.signals import pre_delete
 | 
			
		||||
from portier.common import handlers
 | 
			
		||||
 | 
			
		||||
from rtmp.models import Stream
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -23,3 +25,6 @@ class RestreamConfig(models.Model):
 | 
			
		|||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '{} to {}'.format(self.stream, self.name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=RestreamConfig)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load bootstrap4 %}
 | 
			
		||||
{% load font_awesome %}
 | 
			
		||||
{% load guardian_tags %}
 | 
			
		||||
 | 
			
		||||
{% block 'content' %}
 | 
			
		||||
<h6>{% trans "restreamconfig_configuration_header" %}</h6>
 | 
			
		||||
<hr class="my-4">
 | 
			
		||||
| 
						 | 
				
			
			@ -19,15 +21,20 @@
 | 
			
		|||
  </thead>
 | 
			
		||||
  <tbody>
 | 
			
		||||
    {% for object in object_list %}
 | 
			
		||||
    {% get_obj_perms user for object as "obj_perms" %}
 | 
			
		||||
    {% if "view_restreamconfig" in obj_perms %}
 | 
			
		||||
    <tr>
 | 
			
		||||
      <th scope="ro">{{ object.name }}</th>
 | 
			
		||||
      <td align="right">
 | 
			
		||||
        <div class="btn-group" role="group">
 | 
			
		||||
        <a href="{% url 'restream:restreamconfig_detail' pk=object.pk %}" type="button" class="btn btn-sm btn-primary">{% trans "details" %}</a>
 | 
			
		||||
        {% if "delete_restreamconfig" in obj_perms %}
 | 
			
		||||
        <a href="{% url 'restream:restreamconfig_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-danger">{% fa 'trash' %}</a>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,27 +2,54 @@ from django.urls import reverse_lazy
 | 
			
		|||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.utils.decorators import method_decorator
 | 
			
		||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView
 | 
			
		||||
from guardian.decorators import permission_required_or_403
 | 
			
		||||
from guardian.shortcuts import assign_perm
 | 
			
		||||
 | 
			
		||||
from . import models
 | 
			
		||||
from . import forms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class RestreamConfigList(ListView):
 | 
			
		||||
    model = models.RestreamConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('restream.view_restreamconfig',
 | 
			
		||||
                  (models.RestreamConfig, 'pk', 'pk')),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class RestreamConfigDetail(DetailView):
 | 
			
		||||
    model = models.RestreamConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class RestreamConfigCreate(CreateView):
 | 
			
		||||
    model = models.RestreamConfig
 | 
			
		||||
    fields = ["name", "stream", "target", "active"]
 | 
			
		||||
    form_class = forms.RestreamConfigFilteredStreamForm
 | 
			
		||||
 | 
			
		||||
    def get_form_kwargs(self):
 | 
			
		||||
        kwargs = super().get_form_kwargs()
 | 
			
		||||
        kwargs['user'] = self.request.user
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        valid = super().form_valid(form)
 | 
			
		||||
        if valid:
 | 
			
		||||
            user = self.request.user
 | 
			
		||||
            assign_perm('view_restreamconfig', user, self.object)
 | 
			
		||||
            assign_perm('change_restreamconfig', user, self.object)
 | 
			
		||||
            assign_perm('delete_restreamconfig', user, self.object)
 | 
			
		||||
        return valid
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('restream.delete_restreamconfig',
 | 
			
		||||
                  (models.RestreamConfig, 'pk', 'pk')),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class RestreamConfigDelete(DeleteView):
 | 
			
		||||
    model = models.RestreamConfig
 | 
			
		||||
    success_url = reverse_lazy('restream:restreamconfig_list')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,13 @@
 | 
			
		|||
from django.contrib import admin
 | 
			
		||||
from guardian.admin import GuardedModelAdmin
 | 
			
		||||
from .models import Application, Stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ApplicationAdmin(admin.ModelAdmin):
 | 
			
		||||
class ApplicationAdmin(GuardedModelAdmin):
 | 
			
		||||
    fields = ['name']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StreamAdmin(admin.ModelAdmin):
 | 
			
		||||
class StreamAdmin(GuardedModelAdmin):
 | 
			
		||||
    fields = ['application', 'stream', 'name', 'publish_counter']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,10 @@
 | 
			
		|||
import uuid
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
import uuid
 | 
			
		||||
from django.db.models.signals import pre_delete
 | 
			
		||||
from portier.common import handlers
 | 
			
		||||
 | 
			
		||||
from . import signals
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -66,3 +69,7 @@ class Stream(models.Model):
 | 
			
		|||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Application)
 | 
			
		||||
pre_delete.connect(handlers.remove_obj_perms_connected_with_user, sender=Stream)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
{% load i18n %}
 | 
			
		||||
{% load bootstrap4 %}
 | 
			
		||||
{% load font_awesome %}
 | 
			
		||||
{% load guardian_tags %}
 | 
			
		||||
 | 
			
		||||
{% block 'content' %}
 | 
			
		||||
<h6>{% trans "stream_configuration_header" %}</h6>
 | 
			
		||||
<hr class="my-4">
 | 
			
		||||
| 
						 | 
				
			
			@ -19,15 +21,20 @@
 | 
			
		|||
  </thead>
 | 
			
		||||
  <tbody>
 | 
			
		||||
    {% for object in object_list %}
 | 
			
		||||
    {% get_obj_perms user for object as "obj_perms" %}
 | 
			
		||||
    {% if "view_stream" in obj_perms %}
 | 
			
		||||
    <tr>
 | 
			
		||||
      <th scope="ro">{{ object.name }}</th>
 | 
			
		||||
      <td align="right">
 | 
			
		||||
        <div class="btn-group" role="group">
 | 
			
		||||
        <a href="{% url 'rtmp:stream_detail' pk=object.pk %}" type="button" class="btn btn-sm btn-primary">{% trans "details" %}</a>
 | 
			
		||||
        {% if "delete_stream" in obj_perms %}
 | 
			
		||||
        <a href="{% url 'rtmp:stream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-danger">{% fa 'trash' %}</a>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,8 @@ from django.contrib.admin.utils import NestedObjects
 | 
			
		|||
from django.utils.decorators import method_decorator
 | 
			
		||||
from django.views.decorators.csrf import csrf_exempt
 | 
			
		||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView
 | 
			
		||||
from guardian.decorators import permission_required_or_403
 | 
			
		||||
from guardian.shortcuts import assign_perm
 | 
			
		||||
 | 
			
		||||
from . import models
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,22 +50,41 @@ def callback_srs(request):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('rtmp.add_stream'),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class StreamList(ListView):
 | 
			
		||||
    model = models.Stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('rtmp.view_stream',
 | 
			
		||||
                  (models.Stream, 'pk', 'pk')),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class StreamDetail(DetailView):
 | 
			
		||||
    model = models.Stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('rtmp.add_stream'),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class StreamCreate(CreateView):
 | 
			
		||||
    model = models.Stream
 | 
			
		||||
    fields = ["name", "application"]
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        valid = super().form_valid(form)
 | 
			
		||||
        if valid:
 | 
			
		||||
            user = self.request.user
 | 
			
		||||
            assign_perm('view_stream', user, self.object)
 | 
			
		||||
            assign_perm('change_stream', user, self.object)
 | 
			
		||||
            assign_perm('delete_stream', user, self.object)
 | 
			
		||||
        return valid
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@method_decorator(login_required, name='dispatch')
 | 
			
		||||
@method_decorator(permission_required_or_403('rtmp.delete_stream',
 | 
			
		||||
                  (models.Stream, 'pk', 'pk')),
 | 
			
		||||
                  name='dispatch')
 | 
			
		||||
class StreamDelete(DeleteView):
 | 
			
		||||
    model = models.Stream
 | 
			
		||||
    success_url = reverse_lazy('rtmp:stream_list')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue