Files
dealerhub/backend/apps/core/permissions.py
T

58 lines
2.2 KiB
Python

"""租户授权权限类。
X-Tenant-Id 只选择请求上下文;已认证用户必须拥有该租户的有效 membership。
公开端点显式使用 AllowAny,因此不会被此默认权限类改变。
"""
from django.conf import settings
from rest_framework.permissions import BasePermission
class TenantMembershipPermission(BasePermission):
"""Require an active user membership for the requested tenant."""
message = "当前用户无权访问该租户"
code = "tenant_membership_required"
def has_permission(self, request, view):
user = getattr(request, "user", None)
if not user or not user.is_authenticated:
return False
# API keys are already cryptographically bound to their tenant. Keep
# their existing header-optional contract, but reject an explicit
# attempt to select a different tenant.
auth = getattr(request, "auth", None)
if auth is not None and auth.__class__.__name__ == "APIKey":
key_tenant = getattr(auth, "tenant", None)
requested = request.META.get("HTTP_X_TENANT_ID")
return key_tenant is not None and (
not requested or requested == key_tenant.code
)
# Superusers keep the historical bootstrap contract: they are operators
# for every seeded tenant (seed_initial_data + the 0002 backfill).
if getattr(user, "is_superuser", False):
return True
tenant_code = request.META.get(
"HTTP_X_TENANT_ID", getattr(settings, "TENANT_DEFAULT", "default")
)
if not tenant_code:
return False
from .models import Tenant, TenantMembership
# An unknown (or inactive) tenant is a *parameter* error, not an
# authorization failure. Answering it here would mask it as 403; the
# view layer returns 400「无法识别租户」 instead.
if not Tenant.objects.filter(code=tenant_code, is_active=True).exists():
return True
return TenantMembership.objects.filter(
user=user,
tenant__code=tenant_code,
tenant__is_active=True,
is_active=True,
).exists()