47 lines
1.6 KiB
Python
47 lines
1.6 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
|
|
)
|
|
|
|
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
|
|
|
|
return TenantMembership.objects.filter(
|
|
user=user,
|
|
tenant__code=tenant_code,
|
|
tenant__is_active=True,
|
|
is_active=True,
|
|
).exists()
|