Repository: ephios-dev/ephios

Issue:
Manage permissions for group
As a manager, when editing groups I want to have checkboxes fot "can manage user" and "can manage groups" that control the appropriate permissions on the group.


Discussion:

Resolving patch:
diff --git a/ephios/user_management/forms.py b/ephios/user_management/forms.py
--- a/ephios/user_management/forms.py
+++ b/ephios/user_management/forms.py
@@ -5,7 +5,7 @@
 from django.forms import BooleanField, ModelForm, ModelMultipleChoiceField, inlineformset_factory
 from django.utils.translation import gettext as _
 from django_select2.forms import Select2MultipleWidget, Select2Widget
-from guardian.shortcuts import assign_perm, remove_perm
+from guardian.shortcuts import assign_perm, get_objects_for_group, remove_perm
 
 from ephios.extra.widgets import CustomDateInput
 from ephios.user_management.models import QualificationGrant, UserProfile
@@ -90,14 +90,24 @@ def clean_password(self):
 
 class GroupForm(ModelForm):
     publish_event_for_group = ModelMultipleChoiceField(
-        label=_("Can publish event for group"),
+        label=_("Can publish events for groups"),
         queryset=Group.objects.all(),
         required=False,
         help_text=_("Choose groups that this group can make events visible for."),
         widget=Select2MultipleWidget,
     )
     can_view_past_event = BooleanField(label=_("Can view past events"), required=False)
-    can_add_event = BooleanField(label=_("Can add event"), required=False)
+    can_add_event = BooleanField(label=_("Can add events"), required=False)
+    can_manage_user = BooleanField(
+        label=_("Can manage users"),
+        help_text=_("If checked, users in this group can view, add, edit and delete users."),
+        required=False,
+    )
+    can_manage_group = BooleanField(
+        label=_("Can manage groups"),
+        help_text=_("If checked, users in this group can view, add, edit and delete groups."),
+        required=False,
+    )
     users = ModelMultipleChoiceField(
         label=_("Users"), queryset=UserProfile.objects.all(), widget=MultiUserProfileWidget
     )
@@ -105,6 +115,8 @@ class GroupForm(ModelForm):
     field_order = [
         "name",
         "users",
+        "can_manage_user",
+        "can_manage_group",
         "can_view_past_event",
         "can_add_event",
         "publish_event_for_group",
@@ -114,6 +126,37 @@ class Meta:
         model = Group
         fields = ["name"]
 
+    def __init__(self, **kwargs):
+        if (group := kwargs.get("instance", None)) is not None:
+            kwargs["initial"] = {
+                "users": group.user_set.all(),
+                "can_view_past_event": group.permissions.filter(
+                    codename="view_past_event"
+                ).exists(),
+                "can_add_event": group.permissions.filter(codename="add_event").exists(),
+                "publish_event_for_group": get_objects_for_group(
+                    group, "publish_event_for_group", klass=Group
+                ),
+                "can_manage_user": group.permissions.filter(
+                    codename__in=[
+                        "add_userprofile",
+                        "change_userprofile",
+                        "delete_userprofile",
+                        "view_userprofile",
+                    ]
+                ).exists(),
+                "can_manage_group": group.permissions.filter(
+                    codename__in=[
+                        "add_group",
+                        "change_group",
+                        "delete_group",
+                        "view_group",
+                    ]
+                ).exists(),
+                **kwargs.get("initial", {}),
+            }
+        super().__init__(**kwargs)
+
     def save(self, commit=True):
         group = super().save(commit)
 
@@ -135,6 +178,28 @@ def save(self, commit=True):
             remove_perm("event_management.add_event", group)
             remove_perm("event_management.delete_event", group)
 
+        if self.cleaned_data["can_manage_user"]:
+            assign_perm("user_management.add_userprofile", group)
+            assign_perm("user_management.change_userprofile", group)
+            assign_perm("user_management.delete_userprofile", group)
+            assign_perm("user_management.view_userprofile", group)
+        else:
+            remove_perm("user_management.add_userprofile", group)
+            remove_perm("user_management.change_userprofile", group)
+            remove_perm("user_management.delete_userprofile", group)
+            remove_perm("user_management.view_userprofile", group)
+
+        if self.cleaned_data["can_manage_group"]:
+            assign_perm("auth.add_group", group)
+            assign_perm("auth.change_group", group)
+            assign_perm("auth.delete_group", group)
+            assign_perm("auth.view_group", group)
+        else:
+            remove_perm("auth.add_group", group)
+            remove_perm("auth.change_group", group)
+            remove_perm("auth.delete_group", group)
+            remove_perm("auth.view_group", group)
+
         return group
 
 
diff --git a/ephios/user_management/views.py b/ephios/user_management/views.py
--- a/ephios/user_management/views.py
+++ b/ephios/user_management/views.py
@@ -13,7 +13,6 @@
     UpdateView,
 )
 from django.views.generic.detail import SingleObjectMixin
-from guardian.shortcuts import get_objects_for_group
 
 from ephios.extra.permissions import CustomPermissionRequiredMixin
 from ephios.user_management import mail
@@ -141,15 +140,6 @@ class GroupCreateView(CustomPermissionRequiredMixin, CreateView):
     template_name = "user_management/group_form.html"
     form_class = GroupForm
 
-    def get_form_kwargs(self):
-        kwargs = super().get_form_kwargs()
-        kwargs["initial"] = {
-            "users": UserProfile.objects.none(),
-            "can_add_event": False,
-            "publish_event_for_group": Group.objects.none(),
-        }
-        return kwargs
-
     def get_success_url(self):
         messages.success(
             self.request, _('Group "{group}" created successfully.').format(group=self.object)
@@ -163,20 +153,6 @@ class GroupUpdateView(CustomPermissionRequiredMixin, UpdateView):
     template_name = "user_management/group_form.html"
     form_class = GroupForm
 
-    def get_form_kwargs(self):
-        kwargs = super().get_form_kwargs()
-        kwargs["initial"] = {
-            "users": self.object.user_set.all(),
-            "can_view_past_event": self.object.permissions.filter(
-                codename="view_past_event"
-            ).exists(),
-            "can_add_event": self.object.permissions.filter(codename="add_event").exists(),
-            "publish_event_for_group": get_objects_for_group(
-                self.object, "publish_event_for_group", klass=Group
-            ),
-        }
-        return kwargs
-
     def get_success_url(self):
         messages.success(
             self.request, _('Group "{group}" updated successfully.').format(group=self.object)


Test patch:
diff --git a/tests/user_management/test_views_group.py b/tests/user_management/test_views_group.py
--- a/tests/user_management/test_views_group.py
+++ b/tests/user_management/test_views_group.py
@@ -36,6 +36,22 @@ def test_group_create(self, django_app, groups, manager):
         assert list(group.user_set.all()) == [manager]
         assert not group.permissions.filter(codename="view_past_event").exists()
         assert not group.permissions.filter(codename="add_event").exists()
+        assert not group.permissions.filter(
+            codename__in=[
+                "add_userprofile",
+                "change_userprofile",
+                "delete_userprofile",
+                "view_userprofile",
+            ]
+        ).exists()
+        assert not group.permissions.filter(
+            codename__in=[
+                "add_group",
+                "change_group",
+                "delete_group",
+                "view_group",
+            ]
+        ).exists()
 
     def test_group_create_with_permissions(self, django_app, groups, manager):
         response = django_app.get(reverse("user_management:group_add"), user=manager)
@@ -46,6 +62,8 @@ def test_group_create_with_permissions(self, django_app, groups, manager):
         form["can_view_past_event"] = True
         form["can_add_event"] = True
         form["publish_event_for_group"].select_multiple(texts=["Volunteers"])
+        form["can_manage_user"] = True
+        form["can_manage_group"] = True
         response = form.submit()
         assert response.status_code == 302
         group = Group.objects.get(name=group_name)
@@ -55,6 +73,14 @@ def test_group_create_with_permissions(self, django_app, groups, manager):
         assert "publish_event_for_group" in get_group_perms(
             group, Group.objects.get(name="Volunteers")
         )
+        assert group.permissions.filter(codename="add_userprofile").exists()
+        assert group.permissions.filter(codename="change_userprofile").exists()
+        assert group.permissions.filter(codename="delete_userprofile").exists()
+        assert group.permissions.filter(codename="view_userprofile").exists()
+        assert group.permissions.filter(codename="add_group").exists()
+        assert group.permissions.filter(codename="change_group").exists()
+        assert group.permissions.filter(codename="delete_group").exists()
+        assert group.permissions.filter(codename="view_group").exists()
 
     def test_group_edit(self, django_app, groups, manager):
         group = manager.groups.first()