73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
from contextvars import ContextVar
|
|
from functools import wraps
|
|
|
|
from tenancy.models import Tenant
|
|
|
|
active_tenant_id = ContextVar("netbox_utilities_active_tenant_id", default=None)
|
|
|
|
_PARENT_RELATIONS = (
|
|
"device",
|
|
"virtual_machine",
|
|
"site",
|
|
"location",
|
|
"rack",
|
|
"cluster",
|
|
"circuit",
|
|
"virtual_circuit",
|
|
"tunnel",
|
|
"l2vpn",
|
|
"wireless_lan",
|
|
"power_panel",
|
|
)
|
|
|
|
|
|
def object_matches_tenant(obj, tenant_id, depth=0):
|
|
"""
|
|
Return whether a search result belongs to the selected tenant.
|
|
|
|
Models without tenant semantics are shared reference data and remain visible.
|
|
Child objects inherit tenant membership from a known parent relationship.
|
|
"""
|
|
if obj is None:
|
|
return False
|
|
if isinstance(obj, Tenant):
|
|
return obj.pk == tenant_id
|
|
|
|
field_names = {field.name for field in obj._meta.get_fields()}
|
|
if "tenant" in field_names:
|
|
return getattr(obj, "tenant_id", None) == tenant_id
|
|
|
|
if depth < 2:
|
|
for relation in _PARENT_RELATIONS:
|
|
if relation not in field_names:
|
|
continue
|
|
parent = getattr(obj, relation, None)
|
|
if parent is None:
|
|
return False
|
|
return object_matches_tenant(parent, tenant_id, depth + 1)
|
|
|
|
# Global reference objects (roles, manufacturers, statuses, etc.) are not
|
|
# tenant-owned and must remain usable while a filter is active.
|
|
return True
|
|
|
|
|
|
def install_search_filter():
|
|
"""Install the tenant scope around NetBox's singleton global search backend."""
|
|
from netbox.search.backends import search_backend
|
|
|
|
if getattr(search_backend, "_netbox_utilities_wrapped", False):
|
|
return
|
|
|
|
original_search = search_backend.search
|
|
|
|
@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:
|
|
return results
|
|
return [result for result in results if object_matches_tenant(result.object, tenant_id)]
|
|
|
|
search_backend.search = tenant_aware_search
|
|
search_backend._netbox_utilities_wrapped = True
|