From af78588932f5a09fe18616e2bca0c02160d120ef Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Mon, 6 Apr 2020 16:43:44 +0200
Subject: [PATCH] Add permission rules for Person model

---
 aleksis/core/menus.py                        |  9 +++--
 aleksis/core/rules.py                        | 37 +++++++++++++++-----
 aleksis/core/templates/core/person_full.html | 18 ++++++----
 aleksis/core/views.py                        | 30 +++++++++-------
 4 files changed, 61 insertions(+), 33 deletions(-)

diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py
index f50d559f6..911b5b32a 100644
--- a/aleksis/core/menus.py
+++ b/aleksis/core/menus.py
@@ -124,16 +124,15 @@ MENUS = {
             "url": "#",
             "icon": "people",
             "root": True,
-            "validators": [
-                "menu_generator.validators.is_authenticated",
-                "aleksis.core.util.core_helpers.has_person",
-            ],
+            "validators": [("aleksis.core.util.predicates.permission_validator", "core.view_people_menu")],
             "submenu": [
                 {
                     "name": _("Persons"),
                     "url": "persons",
                     "icon": "person",
-                    "validators": ["menu_generator.validators.is_authenticated"],
+                    "validators": [
+                        ("aleksis.core.util.predicates.permission_validator", "core.view_persons")
+                    ],
                 },
                 {
                     "name": _("Groups"),
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index b2406a0b6..f9b87f3d7 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -1,13 +1,34 @@
+from rules import add_perm, always_allow
 
-from rules import predicate, add_perm
+from aleksis.core.models import Person
+from aleksis.core.util.predicates import (
+    has_person_predicate,
+    has_global_perm,
+    has_any_object,
+    is_person,
+    has_object_perm,
+)
 
-from aleksis.core.util.core_helpers import has_person
 
-print("rules?")
+add_perm("core", always_allow)
 
-@predicate
-def has_person_predicate(user):
-    # return has_person(user)
-    return True
+# View persons
+view_persons_predicate = has_person_predicate & (
+    has_global_perm("core.view_person") | has_any_object("core.view_person", Person)
+)
+add_perm("core.view_persons", view_persons_predicate)
 
-add_perm('core.view_person', has_person_predicate)
\ No newline at end of file
+# View person
+view_person_predicate = has_person_predicate & (
+    has_global_perm("core.view_person") | has_object_perm("core.view_person") | is_person
+)
+add_perm("core.view_person", view_person_predicate)
+
+# Change person
+change_person_predicate = has_person_predicate & (
+    has_global_perm("core.change_person") | has_object_perm("core.change_person")
+)
+add_perm("core.change_person", change_person_predicate)
+
+# People menu (persons + objects)
+add_perm("core.view_people_menu", has_person_predicate & (view_persons_predicate))
diff --git a/aleksis/core/templates/core/person_full.html b/aleksis/core/templates/core/person_full.html
index 8f9ceed60..73e099fea 100644
--- a/aleksis/core/templates/core/person_full.html
+++ b/aleksis/core/templates/core/person_full.html
@@ -2,19 +2,23 @@
 
 {% extends "core/base.html" %}
 
-{% load i18n static cropping %}
+{% load i18n static cropping rules %}
 {% load render_table from django_tables2 %}
 
 {% block browser_title %}{{ person.first_name }} {{ person.last_name }}{% endblock %}
 
 {% block content %}
   <h4>{{ person.first_name }} {{ person.last_name }}</h4>
-  <p>
-    <a href="{% url 'edit_person_by_id' person.id %}" class="btn waves-effect waves-light">
-      <i class="material-icons left">edit</i>
-      {% trans "Edit" %}
-    </a>
-  </p>
+
+  {% has_perm 'core.change_person' user person as can_change_person %}
+  {% if can_change_person %}
+    <p>
+      <a href="{% url 'edit_person_by_id' person.id %}" class="btn waves-effect waves-light">
+        <i class="material-icons left">edit</i>
+        {% trans "Edit" %}
+      </a>
+    </p>
+  {% endif %}
 
   <h5>{% blocktrans %}Contact details{% endblocktrans %}</h5>
   <div class="row">
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 5608d1d60..61548dede 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -7,6 +7,8 @@ from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.translation import ugettext_lazy as _
 
 from django_tables2 import RequestConfig
+from guardian.shortcuts import get_objects_for_user
+from rules.contrib.views import permission_required, objectgetter
 
 from .decorators import admin_required, person_required
 from .forms import (
@@ -50,12 +52,14 @@ def offline(request):
     return render(request, "core/offline.html")
 
 
-@login_required
+@permission_required("core.view_persons")
 def persons(request: HttpRequest) -> HttpResponse:
     context = {}
 
     # Get all persons
-    persons = Person.objects.filter(is_active=True)
+    persons = get_objects_for_user(
+        request.user, "core.view_person", Person.objects.filter(is_active=True)
+    )
 
     # Build table
     persons_table = PersonsTable(persons)
@@ -65,24 +69,24 @@ def persons(request: HttpRequest) -> HttpResponse:
     return render(request, "core/persons.html", context)
 
 
-@login_required
+def get_person_by_pk(request, id_: Optional[int] = None):
+    if id_:
+        return get_object_or_404(Person, pk=id_)
+    else:
+        return request.user.person
+
+
+@permission_required("core.view_person", fn=get_person_by_pk)
 def person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
     context = {}
 
     # Get person and check access
-    try:
-        if id_ is None:
-            person = request.user.person
-        else:
-            person = Person.objects.get(pk=id_)
-    except Person.DoesNotExist as e:
-        # Turn not-found object into a 404 error
-        raise Http404 from e
+    person = get_person_by_pk(request, id_)
 
     context["person"] = person
 
     # Get groups where person is member of
-    groups = Group.objects.filter(members=id_)
+    groups = Group.objects.filter(members=person.pk)
 
     # Build table
     groups_table = GroupsTable(groups)
@@ -158,7 +162,7 @@ def persons_accounts(request: HttpRequest) -> HttpResponse:
     return render(request, "core/persons_accounts.html", context)
 
 
-@admin_required
+@permission_required("core.change_person", fn=objectgetter(Person, "id_"))
 def edit_person(request: HttpRequest, id_: int) -> HttpResponse:
     context = {}
 
-- 
GitLab