baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""租户授权权限类。
|
||||
|
||||
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()
|
||||
Reference in New Issue
Block a user