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.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'guardian',
|
||||||
'django_registration',
|
'django_registration',
|
||||||
'bootstrap4',
|
'bootstrap4',
|
||||||
'fa',
|
'fa',
|
||||||
|
@ -55,6 +56,11 @@ MIDDLEWARE = [
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
'guardian.backends.ObjectPermissionBackend',
|
||||||
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'portier.urls'
|
ROOT_URLCONF = 'portier.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
django>=3.0
|
django>=3.0
|
||||||
django-registration>=3.1
|
django-registration>=3.1
|
||||||
django-bootstrap4
|
django-bootstrap4
|
||||||
|
django-guardian
|
||||||
django-fa
|
django-fa
|
||||||
celery>=4.4
|
celery>=4.4
|
||||||
gunicorn>=20
|
gunicorn>=20
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from guardian.admin import GuardedModelAdmin
|
||||||
from .models import RestreamConfig
|
from .models import RestreamConfig
|
||||||
|
|
||||||
|
|
||||||
class RestreamConfigAdmin(admin.ModelAdmin):
|
class RestreamConfigAdmin(GuardedModelAdmin):
|
||||||
fields = ['name', 'active', 'stream', 'target']
|
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.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
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
|
from rtmp.models import Stream
|
||||||
|
|
||||||
|
@ -23,3 +25,6 @@ class RestreamConfig(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} to {}'.format(self.stream, self.name)
|
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 i18n %}
|
||||||
{% load bootstrap4 %}
|
{% load bootstrap4 %}
|
||||||
{% load font_awesome %}
|
{% load font_awesome %}
|
||||||
|
{% load guardian_tags %}
|
||||||
|
|
||||||
{% block 'content' %}
|
{% block 'content' %}
|
||||||
<h6>{% trans "restreamconfig_configuration_header" %}</h6>
|
<h6>{% trans "restreamconfig_configuration_header" %}</h6>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
|
@ -19,15 +21,20 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
|
{% get_obj_perms user for object as "obj_perms" %}
|
||||||
|
{% if "view_restreamconfig" in obj_perms %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="ro">{{ object.name }}</th>
|
<th scope="ro">{{ object.name }}</th>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
<div class="btn-group" role="group">
|
<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>
|
<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>
|
<a href="{% url 'restream:restreamconfig_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-danger">{% fa 'trash' %}</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -2,27 +2,54 @@ from django.urls import reverse_lazy
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView
|
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 models
|
||||||
|
from . import forms
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
|
||||||
|
name='dispatch')
|
||||||
class RestreamConfigList(ListView):
|
class RestreamConfigList(ListView):
|
||||||
model = models.RestreamConfig
|
model = models.RestreamConfig
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('restream.view_restreamconfig',
|
||||||
|
(models.RestreamConfig, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
class RestreamConfigDetail(DetailView):
|
class RestreamConfigDetail(DetailView):
|
||||||
model = models.RestreamConfig
|
model = models.RestreamConfig
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
|
||||||
|
name='dispatch')
|
||||||
class RestreamConfigCreate(CreateView):
|
class RestreamConfigCreate(CreateView):
|
||||||
model = models.RestreamConfig
|
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(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('restream.delete_restreamconfig',
|
||||||
|
(models.RestreamConfig, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
class RestreamConfigDelete(DeleteView):
|
class RestreamConfigDelete(DeleteView):
|
||||||
model = models.RestreamConfig
|
model = models.RestreamConfig
|
||||||
success_url = reverse_lazy('restream:restreamconfig_list')
|
success_url = reverse_lazy('restream:restreamconfig_list')
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from guardian.admin import GuardedModelAdmin
|
||||||
from .models import Application, Stream
|
from .models import Application, Stream
|
||||||
|
|
||||||
|
|
||||||
class ApplicationAdmin(admin.ModelAdmin):
|
class ApplicationAdmin(GuardedModelAdmin):
|
||||||
fields = ['name']
|
fields = ['name']
|
||||||
|
|
||||||
|
|
||||||
class StreamAdmin(admin.ModelAdmin):
|
class StreamAdmin(GuardedModelAdmin):
|
||||||
fields = ['application', 'stream', 'name', 'publish_counter']
|
fields = ['application', 'stream', 'name', 'publish_counter']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
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
|
from . import signals
|
||||||
|
|
||||||
|
@ -66,3 +69,7 @@ class Stream(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
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 i18n %}
|
||||||
{% load bootstrap4 %}
|
{% load bootstrap4 %}
|
||||||
{% load font_awesome %}
|
{% load font_awesome %}
|
||||||
|
{% load guardian_tags %}
|
||||||
|
|
||||||
{% block 'content' %}
|
{% block 'content' %}
|
||||||
<h6>{% trans "stream_configuration_header" %}</h6>
|
<h6>{% trans "stream_configuration_header" %}</h6>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
|
@ -19,15 +21,20 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
|
{% get_obj_perms user for object as "obj_perms" %}
|
||||||
|
{% if "view_stream" in obj_perms %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="ro">{{ object.name }}</th>
|
<th scope="ro">{{ object.name }}</th>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
<div class="btn-group" role="group">
|
<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>
|
<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>
|
<a href="{% url 'rtmp:stream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-danger">{% fa 'trash' %}</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -9,6 +9,8 @@ from django.contrib.admin.utils import NestedObjects
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView
|
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 models
|
||||||
|
|
||||||
|
@ -48,22 +50,41 @@ def callback_srs(request):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('rtmp.add_stream'),
|
||||||
|
name='dispatch')
|
||||||
class StreamList(ListView):
|
class StreamList(ListView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('rtmp.view_stream',
|
||||||
|
(models.Stream, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
class StreamDetail(DetailView):
|
class StreamDetail(DetailView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('rtmp.add_stream'),
|
||||||
|
name='dispatch')
|
||||||
class StreamCreate(CreateView):
|
class StreamCreate(CreateView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
fields = ["name", "application"]
|
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(login_required, name='dispatch')
|
||||||
|
@method_decorator(permission_required_or_403('rtmp.delete_stream',
|
||||||
|
(models.Stream, 'pk', 'pk')),
|
||||||
|
name='dispatch')
|
||||||
class StreamDelete(DeleteView):
|
class StreamDelete(DeleteView):
|
||||||
model = models.Stream
|
model = models.Stream
|
||||||
success_url = reverse_lazy('rtmp:stream_list')
|
success_url = reverse_lazy('rtmp:stream_list')
|
||||||
|
|
Loading…
Reference in New Issue