From a607ac8cc09f99a2b178e076f9bbe457190068ab Mon Sep 17 00:00:00 2001 From: b12f Date: Thu, 27 Nov 2025 15:52:57 +0100 Subject: [PATCH] cli: add basic group management commands This commit adds four commands, all intended to make working with groups from the cli easier: * `group_create` creates groups and assigns intended permissions * `group_delete` deletes a group * `group_list` lists the names of all groups * `permission_list` lists the names of all permissions --- .../management/commands/group_create.py | 71 +++++++++++++++++++ .../management/commands/group_delete.py | 24 +++++++ .../management/commands/group_list.py | 13 ++++ .../management/commands/permission_list.py | 26 +++++++ 4 files changed, 134 insertions(+) create mode 100644 src/documents/management/commands/group_create.py create mode 100644 src/documents/management/commands/group_delete.py create mode 100644 src/documents/management/commands/group_list.py create mode 100644 src/documents/management/commands/permission_list.py diff --git a/src/documents/management/commands/group_create.py b/src/documents/management/commands/group_create.py new file mode 100644 index 000000000..48b2d7d3a --- /dev/null +++ b/src/documents/management/commands/group_create.py @@ -0,0 +1,71 @@ +from django.contrib import auth +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.contrib.auth.models import Permission +from django.core.management import BaseCommand +from django.db import transaction + +from documents.management.commands.mixins import ProgressBarMixin + + +class Command(ProgressBarMixin, BaseCommand): + help = "Create a group" + + def add_arguments(self, parser): + parser.add_argument( + "name", + help="Name of the group", + ) + + # Named (optional) arguments + parser.add_argument( + "-p", + "--permission", + action="append", + help="Permissions to add to the created group", + ) + # Named (optional) arguments + parser.add_argument( + "-a", + "--all-permissions", + action="store_true", + help="Give this group all available permissions", + ) + self.add_argument_progress_bar_mixin(parser) + + def handle(self, *args, **options): + self.handle_progress_bar_mixin(**options) + with transaction.atomic(): + name = options["name"] + permissions = options["permission"] + setAllPermissions = options["all_permissions"] + + if setAllPermissions: + permissions = set() + # We create (but not persist) a temporary superuser and use it to game the + # system and pull all permissions easily. + tmp_superuser = get_user_model()( + is_active=True, + is_superuser=True, + ) + for backend in auth.get_backends(): + if hasattr(backend, "get_all_permissions"): + permissions.update(backend.get_all_permissions(tmp_superuser)) + + # Output unique list of permissions sorted by permission name. + permissions = sorted(list(permissions)) + + new_group, created = Group.objects.get_or_create(name=name) + if created: + self.stdout.write(f"Created group: {new_group.name}\n") + else: + self.stdout.write(f"Group already exists: {new_group.name}\n") + + for permission in permissions: + [module, codename] = permission.split(".") + permission = Permission.objects.get( + content_type__app_label=module, + codename=codename, + ) + new_group.permissions.add(permission) + self.stdout.write(f"Added permission: {permission}\n") diff --git a/src/documents/management/commands/group_delete.py b/src/documents/management/commands/group_delete.py new file mode 100644 index 000000000..4c8f6be63 --- /dev/null +++ b/src/documents/management/commands/group_delete.py @@ -0,0 +1,24 @@ +from django.contrib.auth.models import Group +from django.core.management import BaseCommand +from django.db import transaction + +from documents.management.commands.mixins import ProgressBarMixin + + +class Command(ProgressBarMixin, BaseCommand): + help = "Delete a group" + + def add_arguments(self, parser): + parser.add_argument( + "name", + help="Name of the group", + ) + + self.add_argument_progress_bar_mixin(parser) + + def handle(self, *args, **options): + self.handle_progress_bar_mixin(**options) + + with transaction.atomic(): + name = options["name"] + Group.objects.filter(name=name).delete() diff --git a/src/documents/management/commands/group_list.py b/src/documents/management/commands/group_list.py new file mode 100644 index 000000000..0973a9cf2 --- /dev/null +++ b/src/documents/management/commands/group_list.py @@ -0,0 +1,13 @@ +from django.contrib.auth.models import Group +from django.core.management import BaseCommand + +from documents.management.commands.mixins import ProgressBarMixin + + +class Command(ProgressBarMixin, BaseCommand): + help = "List all groups" + + def handle(self, *args, **options): + groups = Group.objects.all() + for group in groups: + self.stdout.write(f"{group.name}\n") diff --git a/src/documents/management/commands/permission_list.py b/src/documents/management/commands/permission_list.py new file mode 100644 index 000000000..8021e3dcb --- /dev/null +++ b/src/documents/management/commands/permission_list.py @@ -0,0 +1,26 @@ +# Code taken from https://github.com/timonweb/django-debug-permissions +# Licensed under BSD-3-Clause license + +from django.contrib import auth +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + help = "Get a list of all permissions available in the system" + + def handle(self, *args, **options): + permissions = set() + # We create (but not persist) a temporary superuser and use it to game the + # system and pull all permissions easily. + tmp_superuser = get_user_model()( + is_active=True, + is_superuser=True, + ) + for backend in auth.get_backends(): + if hasattr(backend, "get_all_permissions"): + permissions.update(backend.get_all_permissions(tmp_superuser)) + + # Output unique list of permissions sorted by permission name. + sorted_list_of_permissions = sorted(list(permissions)) + self.stdout.write("\n".join(sorted_list_of_permissions))