feat(ram): RAMGrant 建表迁移 + 菜单过滤 RAM 角色粒度集成(无策略保持全员可见)
This commit is contained in:
1 parent
feaa783885
commit
345abe88bb
2 files changed
+98
-9
No files matched your search
@@ -0,0 +1,43 @@
|
||||
"""创建 RAMGrant 表——RAM 资源访问管理(Resource Access Management)授权记录
|
||||
|
||||
表由 gvsds 主项目管理(主库),gvsdsdk RAMGrant 模型为 managed=False 映射。
|
||||
字段对齐 gvsdsdk/models.py RAMGrant:
|
||||
主体 Principal(role/user)× 资源 Resource(app/menu/data_scope)× 操作 Action
|
||||
(view/use/manage/grant)× 作用域(TenantUUID/CompanyUUID,null=全范围)
|
||||
"""
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gerp', '0029_fk_to_uuidfield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
CREATE TABLE IF NOT EXISTS `RAMGrant` (
|
||||
`GrantUUID` BINARY(16) NOT NULL PRIMARY KEY,
|
||||
`PrincipalType` VARCHAR(16) NOT NULL,
|
||||
`PrincipalUUID` BINARY(16) NOT NULL,
|
||||
`ResourceType` VARCHAR(32) NOT NULL,
|
||||
`ResourceUUID` BINARY(16) NOT NULL,
|
||||
`Action` VARCHAR(16) NOT NULL,
|
||||
`TenantUUID` BINARY(16) NULL,
|
||||
`CompanyUUID` BINARY(16) NULL,
|
||||
`GrantStatus` SMALLINT NOT NULL DEFAULT 1,
|
||||
`GrantBy` BINARY(16) NULL,
|
||||
`GrantTime` DATETIME NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX `uniq_ram_grant` ON `RAMGrant`
|
||||
(`PrincipalType`, `PrincipalUUID`, `ResourceType`, `ResourceUUID`, `Action`, `TenantUUID`, `CompanyUUID`);
|
||||
CREATE INDEX `idx_ram_principal` ON `RAMGrant` (`PrincipalUUID`, `ResourceType`);
|
||||
CREATE INDEX `idx_ram_resource` ON `RAMGrant` (`ResourceType`, `ResourceUUID`);
|
||||
""",
|
||||
reverse_sql="""
|
||||
DROP TABLE IF EXISTS `RAMGrant`;
|
||||
""",
|
||||
),
|
||||
]
|
||||
@@ -163,7 +163,10 @@ class MenuListCompatView(ELTAuthenticatedAPIView):
|
||||
# 未安装应用(租户/公司/个人均未启用)的 Manifest 声明的全部菜单一律隐藏,
|
||||
# 含散挂在其他父节点下的页面(如"邮箱管理"挂公司下)。
|
||||
# 管理员仅应用中心管理(安装/停用)豁免,业务菜单同样按安装状态隐藏。
|
||||
# RAM 角色粒度:配置了 RAM 策略(存在 app 级 view 授权)的 App,
|
||||
# 仅对获得授权的角色可见;无策略的 App 保持全员可见(向后兼容)。
|
||||
try:
|
||||
from django.db.models import Q as _DJQ
|
||||
from ..models import App
|
||||
from ..app_manager import AppManager
|
||||
from ..app_manifests import GetAppManifest
|
||||
@@ -175,22 +178,25 @@ class MenuListCompatView(ELTAuthenticatedAPIView):
|
||||
if _role_obj is not None:
|
||||
current_tenant_uuid = _role_obj.TenantUUID
|
||||
|
||||
def _ToUUIDBytes(value):
|
||||
if value is None:
|
||||
return None
|
||||
if hasattr(value, 'bytes'):
|
||||
return value.bytes
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
return value
|
||||
|
||||
def _CollectAppSub(uuid_str, acc):
|
||||
acc.add(uuid_str)
|
||||
for uid, item in menu_map.items():
|
||||
if item['ParentUUID'] == uuid_str and uid not in acc:
|
||||
_CollectAppSub(uid, acc)
|
||||
|
||||
for _app in App.objects.filter(AppStatus=1):
|
||||
if AppManager.CheckAccess(
|
||||
current_tenant_uuid, user.UserUUID, _app.AppCode,
|
||||
CompanyUUID=current_company,
|
||||
) is not None:
|
||||
continue
|
||||
manifest = GetAppManifest(_app.AppCode)
|
||||
def _HideAppMenus(app_code, manifest):
|
||||
"""收集并移除指定应用 Manifest 声明的全部菜单(含子节点)"""
|
||||
if manifest is None:
|
||||
continue
|
||||
# 收集该应用 Manifest 声明的全部菜单(MenuName 匹配 Id 字段)
|
||||
return
|
||||
app_menu_names = {s.MenuName for s in manifest.Menus}
|
||||
_invalid = set()
|
||||
for _uid, _item in menu_map.items():
|
||||
@@ -198,6 +204,46 @@ class MenuListCompatView(ELTAuthenticatedAPIView):
|
||||
_CollectAppSub(_uid, _invalid)
|
||||
for _u in _invalid:
|
||||
menu_map.pop(_u, None)
|
||||
|
||||
# RAM 策略/授权集合(bytes 维度,一次查询取齐)
|
||||
ram_policy_apps = set()
|
||||
ram_granted_apps = set()
|
||||
try:
|
||||
from gvsdsdk.models import RAMGrant as _SdkRAMGrant
|
||||
from gvsdsdk.auth import RAMManager as _SdkRAMManager
|
||||
_tenant_bytes = _ToUUIDBytes(current_tenant_uuid)
|
||||
_company_bytes = _ToUUIDBytes(current_company)
|
||||
ram_qs = _SdkRAMGrant.objects.filter(
|
||||
ResourceType='app', Action='view', GrantStatus=1,
|
||||
)
|
||||
if _tenant_bytes is not None:
|
||||
ram_qs = ram_qs.filter(
|
||||
_DJQ(TenantUUID__isnull=True) | _DJQ(TenantUUID=_tenant_bytes))
|
||||
if _company_bytes is not None:
|
||||
ram_qs = ram_qs.filter(
|
||||
_DJQ(CompanyUUID__isnull=True) | _DJQ(CompanyUUID=_company_bytes))
|
||||
ram_policy_apps = set(ram_qs.values_list('ResourceUUID', flat=True))
|
||||
if ram_policy_apps and role_uuid_bytes:
|
||||
ram_granted_apps = set(_SdkRAMManager.ListResourcesForPrincipal(
|
||||
role_uuid_bytes, 'app', 'view',
|
||||
_tenant_bytes, _company_bytes,
|
||||
))
|
||||
except Exception:
|
||||
logger.exception('MenuListCompatView RAM 策略解析失败')
|
||||
|
||||
for _app in App.objects.filter(AppStatus=1):
|
||||
_app_uuid_bytes = _ToUUIDBytes(_app.AppUUID)
|
||||
if AppManager.CheckAccess(
|
||||
current_tenant_uuid, user.UserUUID, _app.AppCode,
|
||||
CompanyUUID=current_company,
|
||||
) is None:
|
||||
# 未安装 → 隐藏(现状)
|
||||
_HideAppMenus(_app.AppCode, GetAppManifest(_app.AppCode))
|
||||
continue
|
||||
# 已安装 → RAM 角色粒度过滤:配置了策略但当前角色未授权 → 隐藏
|
||||
if (_app_uuid_bytes in ram_policy_apps
|
||||
and _app_uuid_bytes not in ram_granted_apps):
|
||||
_HideAppMenus(_app.AppCode, GetAppManifest(_app.AppCode))
|
||||
except Exception:
|
||||
logger.exception('MenuListCompatView 应用菜单过滤失败')
|
||||
|
||||
|
||||
Reference in new issue
Block a user