Repository: django/django
File: django/contrib/auth/management/__init__.py (Python)

Code before:
    def __init__(self, app_label, old_model, new_model):
        self.app_label = app_label
        self.old_model = old_model
        self.new_model = new_model
        super(RenamePermission, self).__init__(
            self.rename_forward, self.rename_backward
        )

    def _rename(self, apps, schema_editor, old_model, new_model):
        ContentType = apps.get_model("contenttypes", "ContentType")
        from django.contrib.auth.models import Permission  # real live model import

        db = schema_editor.connection.alias
        if not router.allow_migrate_model(db, Permission):
            return

        ctypes = ContentType.objects.filter(
            app_label=self.app_label, model__icontains=old_model
        )
        permissions = Permission.objects.filter(
            content_type_id__in=[ct.id for ct in ctypes]
        )
        for permission in permissions:
            prefix = permission.codename.split("_")[0]
            new_codename = f"{prefix}_{new_model}"
            new_name = f"Can {prefix} {new_model}"
            if permission.codename != new_codename or permission.name != new_name:
                permission.codename = new_codename
                permission.name = new_name
                try:
                    with transaction.atomic(using=db):
                        permission.save(update_fields={"name", "codename"})
                except IntegrityError:
                    # Skip conflicting permissions.
                    pass

    def rename_forward(self, apps, schema_editor):
        self._rename(apps, schema_editor, self.old_model, self.new_model)

    def rename_backward(self, apps, schema_editor):
        self._rename(apps, schema_editor, self.new_model, self.old_model)


def inject_permission_rename_operations(
    plan,
    verbosity=2,
    interactive=True,
    using=DEFAULT_DB_ALIAS,
    apps=global_apps,
    **kwargs,
):

Review comment (security):
```suggestion
            default_verbose_name = camel_case_to_spaces(new_model)
            new_name = f"Can {prefix} {default_verbose_name}"
```
I think the Permission name uses the model verbose_name which I'm not sure we can access but we can probably do a better job of building. This will need some test updates :+1: 

Code after:
class RenamePermission(migrations.RunPython):
    def __init__(self, app_label, old_model, new_model):
        self.app_label = app_label
        self.old_model = old_model
        self.new_model = new_model
        super(RenamePermission, self).__init__(
            self.rename_forward, self.rename_backward
        )

    def _rename(self, apps, schema_editor, old_model, new_model):
        ContentType = apps.get_model("contenttypes", "ContentType")
        # Use the live Permission model instead of the frozen one, since frozen
        # models do not retain foreign key constraints.
        from django.contrib.auth.models import Permission

        db = schema_editor.connection.alias
        ctypes = ContentType.objects.filter(
            app_label=self.app_label, model__icontains=old_model.lower()
        )
        for permission in Permission.objects.filter(
            content_type_id__in=ctypes.values("id")
        ):
            prefix = permission.codename.split("_")[0]
            default_verbose_name = camel_case_to_spaces(new_model)

            new_codename = f"{prefix}_{new_model.lower()}"
            new_name = f"Can {prefix} {default_verbose_name}"

            if permission.codename != new_codename or permission.name != new_name:
                permission.codename = new_codename
                permission.name = new_name
                try:
                    with transaction.atomic(using=db):
                        permission.save(update_fields={"name", "codename"})
                except IntegrityError:
                    pass

    def rename_forward(self, apps, schema_editor):
        self._rename(apps, schema_editor, self.old_model, self.new_model)

    def rename_backward(self, apps, schema_editor):
        self._rename(apps, schema_editor, self.new_model, self.old_model)


def rename_permissions(
    plan,
    verbosity=2,
    interactive=True,
    using=DEFAULT_DB_ALIAS,
    apps=global_apps,
    **kwargs,