fix(auth): enforce document and govdoc route grants
This commit is contained in:
@@ -51,7 +51,7 @@ _ALLOWED_FEATURES = {
|
||||
_DEFAULT_FEATURES_BY_PROFILE = {
|
||||
"document_review": ["home", "documents", "upload", "rules", "rule_groups"],
|
||||
"contract": ["home", "documents", "upload", "rules", "contract_template_search", "contract_template_list"],
|
||||
"govdoc": ["home", "govdoc_audits", "govdoc_upload", "rule_groups"],
|
||||
"govdoc": ["home", "govdoc_audits", "govdoc_upload", "rules"],
|
||||
"cross_checking": ["cross_checking", "cross_checking_upload", "cross_checking_list"],
|
||||
"custom": ["home", "documents"],
|
||||
}
|
||||
@@ -884,6 +884,8 @@ class EntryModuleAdminServiceImpl(IEntryModuleAdminService):
|
||||
feature = str(item or "").strip()
|
||||
if not feature:
|
||||
continue
|
||||
if MenuProfile == "govdoc" and feature == "rule_groups":
|
||||
feature = "rules"
|
||||
if feature not in _ALLOWED_FEATURES:
|
||||
invalid.append(feature)
|
||||
continue
|
||||
@@ -915,6 +917,8 @@ class EntryModuleAdminServiceImpl(IEntryModuleAdminService):
|
||||
normalized: list[str] = []
|
||||
for item in Features:
|
||||
feature = str(item or "").strip()
|
||||
if MenuProfile == "govdoc" and feature == "rule_groups":
|
||||
feature = "rules"
|
||||
if feature in _ALLOWED_FEATURES and feature not in normalized:
|
||||
normalized.append(feature)
|
||||
return normalized or list(_DEFAULT_FEATURES_BY_PROFILE.get(MenuProfile, _DEFAULT_FEATURES_BY_PROFILE["document_review"]))
|
||||
|
||||
@@ -54,7 +54,7 @@ class HomeServiceImpl(IHomeService):
|
||||
_DEFAULT_FEATURES_BY_PROFILE: dict[str, list[str]] = {
|
||||
"document_review": ["home", "documents", "upload", "rules", "rule_groups"],
|
||||
"contract": ["home", "documents", "upload", "rules", "contract_template_search", "contract_template_list"],
|
||||
"govdoc": ["home", "govdoc_audits", "govdoc_upload", "rule_groups"],
|
||||
"govdoc": ["home", "govdoc_audits", "govdoc_upload", "rules"],
|
||||
"cross_checking": ["cross_checking", "cross_checking_upload", "cross_checking_list"],
|
||||
"custom": ["home", "documents"],
|
||||
}
|
||||
@@ -553,6 +553,8 @@ class HomeServiceImpl(IHomeService):
|
||||
normalized: list[str] = []
|
||||
for item in parsed:
|
||||
feature = str(item or "").strip()
|
||||
if menu_profile == "govdoc" and feature == "rule_groups":
|
||||
feature = "rules"
|
||||
if feature in allowed_features and feature not in normalized:
|
||||
normalized.append(feature)
|
||||
return normalized or list(cls._DEFAULT_FEATURES_BY_PROFILE[menu_profile])
|
||||
|
||||
@@ -323,6 +323,18 @@ class RbacAdminServiceImpl(IRbacAdminService):
|
||||
"is_cache": True,
|
||||
"meta": {"group": "settings"},
|
||||
},
|
||||
{
|
||||
"route_path": "/rule-groups",
|
||||
"route_name": "rule-groups",
|
||||
"component": "rule-groups",
|
||||
"route_title": "评查点分组",
|
||||
"icon": "ri-node-tree",
|
||||
"sort_order": 6,
|
||||
"parent_path": "/settings",
|
||||
"is_hidden": False,
|
||||
"is_cache": True,
|
||||
"meta": {"group": "settings"},
|
||||
},
|
||||
]
|
||||
|
||||
_MANAGEABLE_PERMISSION_BLUEPRINTS: list[dict[str, Any]] = [
|
||||
@@ -332,10 +344,10 @@ class RbacAdminServiceImpl(IRbacAdminService):
|
||||
{"permission_key": "entry_module:update:write", "display_name": "更新入口模块", "module": "entry_module", "resource": "update", "action": "write", "api_method": "PUT", "api_path": "/api/v3/entry-modules/{id}", "route_path": "/entry-modules"},
|
||||
{"permission_key": "entry_module:delete:delete", "display_name": "删除入口模块", "module": "entry_module", "resource": "delete", "action": "delete", "api_method": "DELETE", "api_path": "/api/v3/entry-modules/{id}", "route_path": "/entry-modules"},
|
||||
{"permission_key": "entry_module:image:write", "display_name": "上传入口模块图标", "module": "entry_module", "resource": "image", "action": "write", "api_method": "POST", "api_path": "/api/v3/entry-modules/{id}/image", "route_path": "/entry-modules"},
|
||||
{"permission_key": "doc_type:list:read", "display_name": "文档类型列表", "module": "doc_type", "resource": "list", "action": "read", "api_method": "GET", "api_path": "/api/document-types", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:detail:read", "display_name": "文档类型详情", "module": "doc_type", "resource": "detail", "action": "read", "api_method": "GET", "api_path": "/api/document-types/{id}", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:create:write", "display_name": "创建文档类型", "module": "doc_type", "resource": "create", "action": "write", "api_method": "POST", "api_path": "/api/document-types", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:update:write", "display_name": "更新文档类型", "module": "doc_type", "resource": "update", "action": "write", "api_method": "PUT", "api_path": "/api/document-types/{id}", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:list:read", "display_name": "业务大类列表", "module": "doc_type", "resource": "list", "action": "read", "api_method": "GET", "api_path": "/api/v3/document-type-roots", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:detail:read", "display_name": "业务大类详情", "module": "doc_type", "resource": "detail", "action": "read", "api_method": "GET", "api_path": "/api/v3/document-type-roots/{id}", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:create:write", "display_name": "创建业务大类", "module": "doc_type", "resource": "create", "action": "write", "api_method": "POST", "api_path": "/api/v3/document-type-roots", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:update:write", "display_name": "更新业务大类", "module": "doc_type", "resource": "update", "action": "write", "api_method": "PUT", "api_path": "/api/v3/document-type-roots/{id}", "route_path": "/document-types"},
|
||||
{"permission_key": "doc_type:delete:delete", "display_name": "删除文档类型", "module": "doc_type", "resource": "delete", "action": "delete", "api_method": "DELETE", "api_path": "/api/document-types/{id}", "route_path": "/document-types"},
|
||||
{"permission_key": "rbac:tenants:read", "display_name": "查看租户列表", "module": "rbac", "resource": "tenants", "action": "read", "api_method": "GET", "api_path": "/api/v3/tenants", "route_path": "/tenants"},
|
||||
{"permission_key": "rbac:tenants:create", "display_name": "创建租户", "module": "rbac", "resource": "tenants", "action": "create", "api_method": "POST", "api_path": "/api/v3/tenants", "route_path": "/tenants"},
|
||||
|
||||
@@ -26,6 +26,7 @@ class RbacServiceImpl(IRbacService):
|
||||
"/files",
|
||||
"/documents",
|
||||
"/rules",
|
||||
"/rule-groups",
|
||||
"/rules-files",
|
||||
"/settings",
|
||||
"/entry-modules",
|
||||
@@ -322,6 +323,20 @@ class RbacServiceImpl(IRbacService):
|
||||
"meta": {"group": "settings"},
|
||||
"children": None,
|
||||
},
|
||||
{
|
||||
"id": 1019,
|
||||
"route_path": "/rule-groups",
|
||||
"route_name": "rule-groups",
|
||||
"component": "rule-groups",
|
||||
"parent_id": 1013,
|
||||
"route_title": "评查点分组",
|
||||
"icon": "ri-node-tree",
|
||||
"sort_order": 6,
|
||||
"is_hidden": False,
|
||||
"is_cache": True,
|
||||
"meta": {"group": "settings"},
|
||||
"children": None,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -738,7 +753,8 @@ class RbacServiceImpl(IRbacService):
|
||||
databaseRoutes = await self._loadDatabaseRoutes(Session, roleIds, grantedPermissions)
|
||||
|
||||
if self._isFrontendRouteSetReady(databaseRoutes):
|
||||
routes = self._filterRoutesByMinimalScope(databaseRoutes)
|
||||
grantedRoutePaths = self._collectCurrentFrontendRoutePaths(databaseRoutes)
|
||||
routes = self._filterRoutesByRouteAndPermissionScope(databaseRoutes, grantedRoutePaths, grantedPermissions)
|
||||
else:
|
||||
routes = self._buildCompatibilityRoutes(roleKeys, grantedPermissions)
|
||||
|
||||
@@ -872,6 +888,30 @@ class RbacServiceImpl(IRbacService):
|
||||
filtered.append(routeCopy)
|
||||
return filtered
|
||||
|
||||
def _filterRoutesByRouteAndPermissionScope(
|
||||
self,
|
||||
Routes: list[RbacRouteVO],
|
||||
GrantedRoutePaths: set[str],
|
||||
GrantedPermissions: set[str],
|
||||
) -> list[RbacRouteVO]:
|
||||
"""按角色已勾选路由裁剪,接口权限不能替代子路由勾选。"""
|
||||
filtered: list[RbacRouteVO] = []
|
||||
for route in Routes:
|
||||
if not self._isRoutePathEnabled(route.route_path):
|
||||
continue
|
||||
if route.route_path not in GrantedRoutePaths:
|
||||
continue
|
||||
|
||||
routeCopy = route.model_copy(deep=True)
|
||||
routeCopy.permissions = self._resolvePermissionsForPath(route.route_path, GrantedPermissions)
|
||||
routeCopy.children = self._filterRoutesByRouteAndPermissionScope(
|
||||
route.children or [],
|
||||
GrantedRoutePaths,
|
||||
GrantedPermissions,
|
||||
) or None
|
||||
filtered.append(routeCopy)
|
||||
return filtered
|
||||
|
||||
def _filterBlueprintsByMinimalScope(self, Blueprints: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""按当前最小可用范围裁剪兼容蓝图。"""
|
||||
filtered: list[dict[str, Any]] = []
|
||||
@@ -953,6 +993,14 @@ class RbacServiceImpl(IRbacService):
|
||||
paths.update(self._collectRoutePaths(route.children))
|
||||
return paths
|
||||
|
||||
def _collectCurrentFrontendRoutePaths(self, Routes: list[RbacRouteVO]) -> set[str]:
|
||||
"""收集当前前端真实路由,旧 govdoc-audit 残留授权不映射成新版子路由。"""
|
||||
return {
|
||||
path
|
||||
for path in self._collectRoutePaths(Routes)
|
||||
if not path.startswith("/govdoc-audit/")
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _normalizeMeta(Meta: Any) -> dict | None:
|
||||
"""兼容 meta 为 JSON 字符串、字典或空值的情况。"""
|
||||
|
||||
Reference in New Issue
Block a user