diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 91716d0b..cc73601a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,9 @@ Change Log Unreleased ********** +* Register ``CasbinRule`` model in the Django admin. +* Register ``ExtendedCasbinRule`` model in the Django admin as an inline model of ``CasbinRule``. + 0.15.0 - 2025-11-11 ******************** diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py new file mode 100644 index 00000000..e2285158 --- /dev/null +++ b/openedx_authz/admin.py @@ -0,0 +1,50 @@ +"""Admin configuration for openedx_authz.""" + +from casbin_adapter.models import CasbinRule +from django import forms +from django.contrib import admin + +from openedx_authz.models import ExtendedCasbinRule + + +class CasbinRuleForm(forms.ModelForm): + """Custom form for CasbinRule to make v3, v4, v5 fields optional.""" + + class Meta: + """Meta class for CasbinRuleForm.""" + + model = CasbinRule + fields = "__all__" + + def __init__(self, *args, **kwargs): + """Initialize CasbinRuleForm.""" + super().__init__(*args, **kwargs) + # Make v2, v3, v4, v5 optional in the form + # These fields are not always required depending on the policy type + self.fields["v2"].required = False + self.fields["v3"].required = False + self.fields["v4"].required = False + self.fields["v5"].required = False + + +class ExtendedCasbinRuleInline(admin.StackedInline): + """Inline admin for ExtendedCasbinRule to display additional metadata.""" + + model = ExtendedCasbinRule + extra = 0 + fields = ("casbin_rule_key", "scope", "subject", "description", "metadata", "created_at", "updated_at") + readonly_fields = ("casbin_rule_key", "scope", "subject", "created_at", "updated_at") + can_delete = False + + +@admin.register(CasbinRule) +class CasbinRuleAdmin(admin.ModelAdmin): + """Admin for CasbinRule to display additional metadata.""" + + form = CasbinRuleForm + list_display = ("id", "ptype", "v0", "v1", "v2", "v3", "v4", "v5") + search_fields = ("ptype", "v0", "v1", "v2", "v3", "v4", "v5") + list_filter = ("ptype",) + # TODO: In a future, possibly we should only show an inline for the rules that + # have an extended rule, and show the subject and scope information in detail. + inlines = [ExtendedCasbinRuleInline] diff --git a/openedx_authz/models/core.py b/openedx_authz/models/core.py index 064eb4c8..326ecbc3 100644 --- a/openedx_authz/models/core.py +++ b/openedx_authz/models/core.py @@ -44,6 +44,7 @@ def get_registry(cls) -> dict[str, type["BaseRegistryModel"]]: """ return cls._registry + class ScopeManager(models.Manager): """Custom manager for Scope model that handles polymorphic behavior."""