feat: add tenant groups and required tenancy

This commit is contained in:
2026-07-28 16:57:52 +02:00
parent 54f6a3f6c9
commit 35e06ee004
17 changed files with 427 additions and 75 deletions
+50 -14
View File
@@ -1,10 +1,11 @@
from django.urls import Resolver404, resolve
from tenancy.models import Tenant
from tenancy.models import Tenant, TenantGroup
from .runtime import tenant_filter_enabled
from .tenant_scope import active_tenant_id
from .tenant_scope import ActiveTenantScope, active_tenant_scope
SESSION_TENANT_KEY = "netbox_utilities.selected_tenant_id"
SESSION_TENANT_GROUP_KEY = "netbox_utilities.selected_tenant_group_id"
class GlobalTenantFilterMiddleware:
@@ -15,35 +16,53 @@ class GlobalTenantFilterMiddleware:
def __call__(self, request):
request.netbox_utilities_tenant = None
tenant_id = self._get_selected_tenant_id(request)
token = active_tenant_id.set(tenant_id)
request.netbox_utilities_tenant_group = None
scope = self._get_selected_scope(request)
token = active_tenant_scope.set(scope)
try:
if tenant_id is not None and request.method in {"GET", "HEAD"}:
self._inject_filter_parameter(request, tenant_id)
if scope is not None and request.method in {"GET", "HEAD"}:
self._inject_filter_parameter(request, scope)
return self.get_response(request)
finally:
active_tenant_id.reset(token)
active_tenant_scope.reset(token)
@staticmethod
def _get_selected_tenant_id(request):
def _get_selected_scope(request):
if not tenant_filter_enabled() or not request.user.is_authenticated:
request.session.pop(SESSION_TENANT_KEY, None)
GlobalTenantFilterMiddleware.clear_scope(request)
return None
tenant_group_id = request.session.get(SESSION_TENANT_GROUP_KEY)
if tenant_group_id is not None:
group = TenantGroup.objects.restrict(request.user, "view").filter(pk=tenant_group_id).first()
if group is None:
GlobalTenantFilterMiddleware.clear_scope(request)
return None
descendant_groups = group.get_descendants(include_self=True)
group_ids = frozenset(descendant_groups.values_list("pk", flat=True))
tenant_ids = Tenant.objects.filter(group_id__in=group_ids).values_list("pk", flat=True)
request.netbox_utilities_tenant_group = group
return ActiveTenantScope("group", group.pk, frozenset(tenant_ids), group_ids)
tenant_id = request.session.get(SESSION_TENANT_KEY)
if tenant_id is None:
return None
tenant = Tenant.objects.restrict(request.user, "view").filter(pk=tenant_id).first()
if tenant is None:
request.session.pop(SESSION_TENANT_KEY, None)
GlobalTenantFilterMiddleware.clear_scope(request)
return None
request.netbox_utilities_tenant = tenant
return tenant.pk
return ActiveTenantScope("tenant", tenant.pk, frozenset({tenant.pk}))
@staticmethod
def _inject_filter_parameter(request, tenant_id):
def clear_scope(request):
request.session.pop(SESSION_TENANT_KEY, None)
request.session.pop(SESSION_TENANT_GROUP_KEY, None)
@staticmethod
def _inject_filter_parameter(request, scope):
try:
match = resolve(request.path_info)
except Resolver404:
@@ -54,15 +73,32 @@ class GlobalTenantFilterMiddleware:
return
parameter = None
values = []
if match.view_name == "tenancy:tenant_list":
if scope.kind == "tenant":
parameter = "id"
values = [scope.object_id]
else:
parameter = "group_id"
values = [scope.object_id]
elif match.view_name == "tenancy:tenantgroup_list" and scope.kind == "group":
parameter = "id"
values = list(scope.group_ids) or [scope.object_id]
else:
view_class = getattr(match.func, "view_class", None)
filterset = getattr(view_class, "filterset", None)
if filterset and "tenant_id" in getattr(filterset, "base_filters", {}):
base_filters = getattr(filterset, "base_filters", {}) if filterset else {}
if scope.kind == "tenant" and "tenant_id" in base_filters:
parameter = "tenant_id"
values = [scope.object_id]
elif scope.kind == "group" and "tenant_group_id" in base_filters:
parameter = "tenant_group_id"
values = [scope.object_id]
elif scope.kind == "group" and "tenant_id" in base_filters:
parameter = "tenant_id"
values = list(scope.tenant_ids) or [0]
if parameter:
query = request.GET.copy()
query.setlist(parameter, [str(tenant_id)])
query.setlist(parameter, [str(value) for value in values])
request.GET = query