diff --git a/core/migrations/0008_auto_20151127_1007.py b/core/migrations/0008_auto_20151127_1007.py new file mode 100644 index 00000000..36ff225e --- /dev/null +++ b/core/migrations/0008_auto_20151127_1007.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0007_auto_20151126_1613'), + ] + + operations = [ + migrations.RemoveField( + model_name='page', + name='view_group', + ), + migrations.AddField( + model_name='page', + name='view_group', + field=models.ManyToManyField(default=1, to='core.Group', related_name='viewable_pages'), + ), + ] diff --git a/core/migrations/0009_auto_20151127_1007.py b/core/migrations/0009_auto_20151127_1007.py new file mode 100644 index 00000000..8b4a35f1 --- /dev/null +++ b/core/migrations/0009_auto_20151127_1007.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_auto_20151127_1007'), + ] + + operations = [ + migrations.RemoveField( + model_name='page', + name='edit_group', + ), + migrations.AddField( + model_name='page', + name='edit_group', + field=models.ManyToManyField(to='core.Group', related_name='editable_pages', default=1), + ), + ] diff --git a/core/models.py b/core/models.py index 059cdbb3..81e30a44 100644 --- a/core/models.py +++ b/core/models.py @@ -154,9 +154,11 @@ class Page(models.Model): # playing with a Page object, use get_full_name() instead! full_name = models.CharField(_('page name'), max_length=255, blank=True) +# TODO: Rightable abstract class from which every object with right needed will be able to be child of +# Put thoses 3 attributes there owner_group = models.ForeignKey(Group, related_name="owned_pages", default=1) - edit_group = models.ForeignKey(Group, related_name="editable_pages", default=1) - view_group = models.ForeignKey(Group, related_name="viewable_pages", default=1) + edit_group = models.ManyToManyField(Group, related_name="editable_pages", default=1) + view_group = models.ManyToManyField(Group, related_name="viewable_pages", default=1) class Meta: unique_together = ('name', 'parent') diff --git a/core/views/__init__.py b/core/views/__init__.py index 364ef9cc..2acc162f 100644 --- a/core/views/__init__.py +++ b/core/views/__init__.py @@ -1,4 +1,65 @@ + +from django.http import HttpResponseForbidden +from django.core.exceptions import PermissionDenied +from django.views.generic.base import View + +# TODO: see models.py's TODO! +class CanEditPropMixin(View): + """ + This view is made to protect any child view that would be showing some properties of an object that are restricted + to only the owner group of the given object. + In other word, you can make a view with this view as parent, and it would be retricted to the users that are in the + object's owner_group + """ + def dispatch(self, request, *arg, **kwargs): + res = super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs) + obj = self.object + user = self.request.user + if obj is None: + return res + if user.is_superuser or user.groups.filter(name=obj.owner_group.name).exists(): + return res + return HttpResponseForbidden("403, Forbidden") + +class CanEditMixin(CanEditPropMixin): + """ + This view makes exactly the same this as its direct parent, but checks the group on the edit_group field of the + object + """ + def dispatch(self, request, *arg, **kwargs): + res = super(CanEditMixin, self).dispatch(request, *arg, **kwargs) + if res.status_code != 403: + return res + obj = self.object + user = self.request.user + if obj is None: + return res + for g in obj.edit_group.all(): + if user.groups.filter(name=g.name).exists(): + return super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs) + return HttpResponseForbidden("403, Forbidden") + +class CanViewMixin(CanEditMixin): + """ + This view still makes exactly the same this as its direct parent, but checks the group on the view_group field of + the object + """ + def dispatch(self, request, *arg, **kwargs): + res = super(CanViewMixin, self).dispatch(request, *arg, **kwargs) + if res.status_code != 403: + return res + obj = self.object + user = self.request.user + if obj is None: + return res + for g in obj.view_group.all(): + if user.groups.filter(name=g.name).exists(): + return super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs) + raise PermissionDenied + return HttpResponseForbidden("403, Forbidden") + from .user import * from .page import * from .site import * from .group import * + diff --git a/core/views/forms.py b/core/views/forms.py index 29911193..f9a1f47d 100644 --- a/core/views/forms.py +++ b/core/views/forms.py @@ -33,6 +33,17 @@ class UserGroupsForm(forms.ModelForm): 'user_permissions': CheckboxSelectMultiple, } +class PagePropForm(forms.ModelForm): + error_css_class = 'error' + required_css_class = 'required' + class Meta: + model = Page + fields = ['parent', 'name', 'owner_group', 'edit_group', 'view_group', ] + widgets = { + 'edit_group': CheckboxSelectMultiple, + 'view_group': CheckboxSelectMultiple, + } + class GroupEditForm(forms.ModelForm): error_css_class = 'error' required_css_class = 'required' diff --git a/core/views/page.py b/core/views/page.py index 6bffcab3..f32907ac 100644 --- a/core/views/page.py +++ b/core/views/page.py @@ -6,6 +6,8 @@ from django.contrib.auth.decorators import login_required, permission_required from django.utils.decorators import method_decorator from core.models import Page +from core.views.forms import PagePropForm +from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin class PageListView(ListView): model = Page @@ -14,12 +16,23 @@ class PageListView(ListView): context = super(PageListView, self).get_context_data(**kwargs) return context -class PageView(DetailView): - model = Page +# Define some right management callable for user_passes_test +def user_can_view(as_view): + def guy(*arg, **kwargs): + res = self.as_view(*arg, **kwargs) - @method_decorator(permission_required('core.can_view')) - def dispatch(self, *args, **kwargs): - return super(PageView, self).dispatch(*args, **kwargs) + user = self.request.user + obj = self.page + for g in obj.view_group.all(): + if g in user.groups.all(): + print("Allowed") + return res + print("Not allowed") + return res + return guy + +class PageView(CanViewMixin, DetailView): + model = Page def get_object(self): self.page = Page.get_page_by_full_name(self.kwargs['page_name']) @@ -34,14 +47,11 @@ class PageView(DetailView): context['new_page'] = self.kwargs['page_name'] return context -class PagePropView(UpdateView): +class PagePropView(CanEditPropMixin, UpdateView): model = Page - fields = ['parent', 'name', 'owner_group', 'edit_group', 'view_group', ] + form_class = PagePropForm template_name_suffix = '_prop' - def __init__(self, *args, **kwargs): - super(PagePropView, self).__init__(*args, **kwargs) - def get_object(self): page_name = self.kwargs['page_name'] p = Page.get_page_by_full_name(page_name) @@ -66,7 +76,7 @@ class PagePropView(UpdateView): context['new_page'] = self.kwargs['page_name'] return context -class PageEditView(UpdateView): +class PageEditView(CanEditMixin, UpdateView): model = Page fields = ['title', 'content',] template_name_suffix = '_edit'