feat(asset): 公司资产权限收敛——仅管理员可见

- 菜单:'公司资产'目录仅 IsCompanyAdmin(admin:company/tenant/super)可见,普通用户隐藏
- 数据范围:FileAssetViewSet 按角色——admin:super 全部 / admin:tenant 本租户所有公司 / admin:company 本公司 / 其他 403
- 上传:Init 管理员校验;Complete/SimpleUpload 归属公司强制解析(租户管理员可指定本租户公司,公司管理员仅本公司)
This commit is contained in:
TermiNexus committed 2026-08-25 09:01:38 +08:00
1 parent 6ec24c8bb3
commit 8775cb37d9
3 files changed
+82 -2

No files matched your search

+43 -2
View File
@@ -11,10 +11,51 @@ from gvsdsdk.model_utils import UUIDObj
from . import ELTAuthenticatedAPIView
def _RequireAssetAdmin(request):
"""公司资产上传仅公司管理员/租户管理员可用"""
from rest_framework.exceptions import PermissionDenied
from gvsdsdk.auth.admin_service import IsCompanyAdmin
if not IsCompanyAdmin(request=request):
raise PermissionDenied('需要公司管理员或租户管理员权限')
def _ResolveAssetCompany(request):
"""上传归属公司解析:仅公司管理员/租户管理员可用。
- 租户管理员:可指定本租户任意公司;不传则用当前公司上下文
- 公司管理员:仅本公司(前端传入必须匹配)
- 其他用户:403
"""
from rest_framework.exceptions import PermissionDenied
from gvsdsdk.auth.admin_service import IsTenantAdmin, IsCompanyAdmin
from gvsdsdk.models import Company as SdkCompany
if not IsCompanyAdmin(request=request):
raise PermissionDenied('需要公司管理员或租户管理员权限')
claimed = (request.data.get('CompanyUUID') or '').strip()
claimed_bytes = UUIDObj(claimed).bytes if claimed else None
cur_company = getattr(request, 'current_company_uuid', None)
if IsTenantAdmin(request=request):
tenant_uuid = getattr(request, 'current_tenant_uuid', None)
if claimed_bytes:
tb = UUIDObj(tenant_uuid).bytes if tenant_uuid else None
if tb is None or not SdkCompany.objects.filter(CompanyUUID=claimed_bytes, TenantUUID=tb).exists():
raise PermissionDenied('目标公司不在本租户')
return claimed_bytes
if cur_company:
return UUIDObj(cur_company).bytes
raise PermissionDenied('缺少 CompanyUUID')
if cur_company:
cb = UUIDObj(cur_company).bytes
if claimed_bytes and claimed_bytes != cb:
raise PermissionDenied('仅可上传到本公司')
return cb
raise PermissionDenied('缺少公司上下文')
class AssetUploadInitView(ELTAuthenticatedAPIView):
"""上传初始化视图——创建分片上传会话"""
def post(self, request):
_RequireAssetAdmin(request)
file_name = request.data.get('FileName', '')
file_size = request.data.get('FileSize', 0)
mime_type = request.data.get('MimeType', 'application/octet-stream')
@@ -134,7 +175,7 @@ class AssetUploadCompleteView(ELTAuthenticatedAPIView):
total_size += len(data)
file_sha1 = sha1.hexdigest()
user_uuid = request.user.UserUUID
company_uuid = request.data.get('CompanyUUID')
company_uuid = _ResolveAssetCompany(request)
from gvsdsdk.auth.managers import AssetManager
asset, created = AssetManager.EnsureAsset(
file_sha1, file_name, total_size, mime_type, final_path,
@@ -182,7 +223,7 @@ class AssetSimpleUploadView(ELTAuthenticatedAPIView):
file_name = upload_file.name
mime_type = upload_file.content_type or 'application/octet-stream'
user_uuid = request.user.UserUUID
company_uuid = request.data.get('CompanyUUID')
company_uuid = _ResolveAssetCompany(request)
sha1 = hashlib.sha1()
final_dir = os.path.join(settings.MEDIA_ROOT, 'assets')
+9
View File
@@ -128,6 +128,15 @@ class MenuListCompatView(ELTAuthenticatedAPIView):
perm_attr_str = item['PermAttr']
perm_attrs = [a.strip() for a in perm_attr_str.split(';') if a.strip()] if perm_attr_str else []
if not perm_attrs:
# 公司资产目录:仅公司管理员/租户管理员可见(普通用户隐藏)
if item['Title'] == '公司资产':
from gvsdsdk.auth.admin_service import IsCompanyAdmin
try:
if not IsCompanyAdmin(request=request, user=user, role_uuid=role_uuid_bytes):
no_perm_uuids.add(uuid_str)
except Exception:
logger.exception('MenuListCompatView 公司资产菜单校验异常 uuid=%s', uuid_str)
no_perm_uuids.add(uuid_str)
continue
if 'admin:super' in perm_attrs:
from gvsdsdk.auth.admin_service import IsSuperAdmin
+30
View File
@@ -574,6 +574,36 @@ class FileAssetViewSet(CompanyScopedMixin, viewsets.ModelViewSet):
serializer_class = FileAssetSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""公司资产数据范围:
- 跨租户管理员(admin:super):全部
- 租户管理员(admin:tenant):本租户所有公司资产
- 公司管理员(admin:company):本公司资产
- 其他用户:无权限(403)
"""
from rest_framework.exceptions import PermissionDenied
from gvsdsdk.auth.admin_service import IsSuperAdmin, IsTenantAdmin, IsCompanyAdmin
from gvsdsdk.models import Company as SdkCompany
from gvsdsdk.model_utils import UUIDObj as _U
qs = SdkFileAsset.objects.all()
user = getattr(self.request, 'user', None)
if IsSuperAdmin(request=self.request, user=user):
return qs
if IsTenantAdmin(request=self.request, user=user):
tenant_uuid = getattr(self.request, 'current_tenant_uuid', None)
if not tenant_uuid:
return qs.none()
tb = _U(tenant_uuid).bytes
companies = SdkCompany.objects.filter(TenantUUID=tb).values_list('CompanyUUID', flat=True)
company_bytes = [c.bytes if hasattr(c, 'bytes') else c for c in companies]
return qs.filter(CompanyUUID__in=company_bytes)
if IsCompanyAdmin(request=self.request, user=user):
company_uuid = getattr(self.request, 'current_company_uuid', None)
if not company_uuid:
return qs.none()
return qs.filter(CompanyUUID=_U(company_uuid).bytes)
raise PermissionDenied('需要公司管理员或租户管理员权限')
def list(self, request, *args, **kwargs):
page_num = int(request.query_params.get('page', 1))
page_size = int(request.query_params.get('pageSize', 24))