add object permissions with guardian; users only have access to their own streams/restreamconfigs

This commit is contained in:
Jan Koppe 2020-05-01 12:38:57 +02:00
parent b16e9c955c
commit 730889b9a1
Signed by: thunfisch
GPG Key ID: BE935B0735A2129B
12 changed files with 114 additions and 5 deletions

View File

@ -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()

View File

@ -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 = [

View File

@ -1,6 +1,7 @@
django>=3.0
django-registration>=3.1
django-bootstrap4
django-guardian
django-fa
celery>=4.4
gunicorn>=20

View File

@ -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']

16
restream/forms.py Normal file
View File

@ -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')

View File

@ -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)

View File

@ -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>

View File

@ -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')

View File

@ -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']

View File

@ -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)

View File

@ -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>

View File

@ -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')