feat: add tenant groups and required tenancy
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
from contextvars import ContextVar
|
||||
from dataclasses import dataclass
|
||||
from functools import wraps
|
||||
|
||||
from tenancy.models import Tenant
|
||||
|
||||
active_tenant_id = ContextVar("netbox_utilities_active_tenant_id", default=None)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ActiveTenantScope:
|
||||
kind: str
|
||||
object_id: int
|
||||
tenant_ids: frozenset[int]
|
||||
group_ids: frozenset[int] = frozenset()
|
||||
|
||||
|
||||
active_tenant_scope = ContextVar("netbox_utilities_active_tenant_scope", default=None)
|
||||
|
||||
_PARENT_RELATIONS = (
|
||||
"device",
|
||||
@@ -22,6 +32,10 @@ _PARENT_RELATIONS = (
|
||||
|
||||
|
||||
def object_matches_tenant(obj, tenant_id, depth=0):
|
||||
return object_matches_tenant_ids(obj, frozenset({tenant_id}), depth=depth)
|
||||
|
||||
|
||||
def object_matches_tenant_ids(obj, tenant_ids, depth=0):
|
||||
"""
|
||||
Return whether a search result belongs to the selected tenant.
|
||||
|
||||
@@ -31,11 +45,11 @@ def object_matches_tenant(obj, tenant_id, depth=0):
|
||||
if obj is None:
|
||||
return False
|
||||
if isinstance(obj, Tenant):
|
||||
return obj.pk == tenant_id
|
||||
return obj.pk in tenant_ids
|
||||
|
||||
field_names = {field.name for field in obj._meta.get_fields()}
|
||||
if "tenant" in field_names:
|
||||
return getattr(obj, "tenant_id", None) == tenant_id
|
||||
return getattr(obj, "tenant_id", None) in tenant_ids
|
||||
|
||||
if depth < 2:
|
||||
for relation in _PARENT_RELATIONS:
|
||||
@@ -44,7 +58,7 @@ def object_matches_tenant(obj, tenant_id, depth=0):
|
||||
parent = getattr(obj, relation, None)
|
||||
if parent is None:
|
||||
return False
|
||||
return object_matches_tenant(parent, tenant_id, depth + 1)
|
||||
return object_matches_tenant_ids(parent, tenant_ids, depth + 1)
|
||||
|
||||
# Global reference objects (roles, manufacturers, statuses, etc.) are not
|
||||
# tenant-owned and must remain usable while a filter is active.
|
||||
@@ -63,10 +77,10 @@ def install_search_filter():
|
||||
@wraps(original_search)
|
||||
def tenant_aware_search(*args, **kwargs):
|
||||
results = original_search(*args, **kwargs)
|
||||
tenant_id = active_tenant_id.get()
|
||||
if tenant_id is None:
|
||||
scope = active_tenant_scope.get()
|
||||
if scope is None:
|
||||
return results
|
||||
return [result for result in results if object_matches_tenant(result.object, tenant_id)]
|
||||
return [result for result in results if object_matches_tenant_ids(result.object, scope.tenant_ids)]
|
||||
|
||||
search_backend.search = tenant_aware_search
|
||||
search_backend._netbox_utilities_wrapped = True
|
||||
|
||||
Reference in New Issue
Block a user