From c6fd6a1bb679eb3b7c65a060c8c0d8286ce92e65 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 11 Nov 2025 16:25:54 -0500 Subject: [PATCH 1/8] feat: add admin configuration for CasbinRule model in openedx_authz --- openedx_authz/admin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 openedx_authz/admin.py diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py new file mode 100644 index 00000000..1519d253 --- /dev/null +++ b/openedx_authz/admin.py @@ -0,0 +1,11 @@ +"""Admin configuration for openedx_authz.""" + +from casbin_adapter.models import CasbinRule +from django.contrib import admin + + +@admin.register(CasbinRule) +class CasbinRuleAdmin(admin.ModelAdmin): + list_display = ("id", "ptype", "v0", "v1", "v2", "v3", "v4", "v5") + search_fields = ("ptype", "v0", "v1", "v2", "v3", "v4", "v5") + list_filter = ("ptype",) From 3d0923f0e5eb324d8a234271fcd6abcbc363bcc3 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 11 Nov 2025 16:34:26 -0500 Subject: [PATCH 2/8] chore: add changelog entry --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 91716d0b..e87c9013 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,8 @@ Change Log Unreleased ********** +* Register ``CasbinRule`` model in the Django admin. + 0.15.0 - 2025-11-11 ******************** From 93670935f55cee6b6eb3012dec4bcecc095d5cec Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 12 Nov 2025 11:43:50 -0500 Subject: [PATCH 3/8] feat: add inline admin for ExtendedCasbinRule --- openedx_authz/admin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py index 1519d253..77b9e8d6 100644 --- a/openedx_authz/admin.py +++ b/openedx_authz/admin.py @@ -3,9 +3,22 @@ from casbin_adapter.models import CasbinRule from django.contrib import admin +from openedx_authz.models import ExtendedCasbinRule + + +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): list_display = ("id", "ptype", "v0", "v1", "v2", "v3", "v4", "v5") search_fields = ("ptype", "v0", "v1", "v2", "v3", "v4", "v5") list_filter = ("ptype",) + inlines = [ExtendedCasbinRuleInline] From 859b930bc7afb8a9a6abf0b323796a5a102c5cd2 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 12 Nov 2025 11:48:38 -0500 Subject: [PATCH 4/8] chore: update changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e87c9013..cc73601a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ 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 ******************** From 830253b10be4105edd0b3828ca3797d650f1a86c Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 12 Nov 2025 12:13:07 -0500 Subject: [PATCH 5/8] feat: implement custom form for CasbinRule to make optional fields --- openedx_authz/admin.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py index 77b9e8d6..e7b33318 100644 --- a/openedx_authz/admin.py +++ b/openedx_authz/admin.py @@ -1,11 +1,29 @@ """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: + model = CasbinRule + fields = "__all__" + + def __init__(self, *args, **kwargs): + 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.""" @@ -18,6 +36,9 @@ class ExtendedCasbinRuleInline(admin.StackedInline): @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",) From 7277f6e4308102f4c8e86a9c8473c71446f2cf1f Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 12 Nov 2025 12:18:25 -0500 Subject: [PATCH 6/8] chore: add missing docstring --- openedx_authz/admin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py index e7b33318..655f7767 100644 --- a/openedx_authz/admin.py +++ b/openedx_authz/admin.py @@ -11,6 +11,8 @@ 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__" From 621328383c5281afe6518bcbb295593e6a4ba4d0 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 12 Nov 2025 12:21:18 -0500 Subject: [PATCH 7/8] chore: add another missing docstring --- openedx_authz/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py index 655f7767..f656a808 100644 --- a/openedx_authz/admin.py +++ b/openedx_authz/admin.py @@ -17,6 +17,7 @@ class Meta: 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 From 8e581effc1a346a3d3b56f1998501c4d5c4b1d8f Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 13 Nov 2025 10:46:29 -0500 Subject: [PATCH 8/8] chore: add TODO comment for future enhancement --- openedx_authz/admin.py | 2 ++ openedx_authz/models/core.py | 1 + 2 files changed, 3 insertions(+) diff --git a/openedx_authz/admin.py b/openedx_authz/admin.py index f656a808..e2285158 100644 --- a/openedx_authz/admin.py +++ b/openedx_authz/admin.py @@ -45,4 +45,6 @@ class CasbinRuleAdmin(admin.ModelAdmin): 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."""