chore: initial commit — leaudit-platform project skeleton
17-table PostgreSQL schema with full Chinese column comments, FastAPI project structure (admin/common/modules), DSL rule files, and schema migration scripts.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""权限服务实现。
|
||||
|
||||
从旧项目 app/rbac/permission_checker_v2.py 迁移,业务逻辑完全不变。
|
||||
- 数据库驱动 GRANT/DENY 机制(DENY 优先级更高)
|
||||
- 支持通配符:document:*:*、*:*:* 等
|
||||
- 实时查询(无 Redis 缓存)
|
||||
仅改造为 SQLAlchemy 会话 + 项目统一配置。
|
||||
"""
|
||||
|
||||
from fastapi_common.fastapi_common_logger import logger
|
||||
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
|
||||
|
||||
from fastapi_modules.fastapi_leaudit.services.permissionService import IPermissionService
|
||||
|
||||
|
||||
class PermissionServiceImpl(IPermissionService):
|
||||
"""权限检查服务实现。"""
|
||||
|
||||
async def CheckPermission(self, UserId: int, PermissionKey: str) -> bool:
|
||||
"""检查用户是否拥有指定权限。
|
||||
|
||||
GRANT/DENY 优先级:
|
||||
1. 精确 DENY → 拒绝
|
||||
2. 通配符 DENY → 拒绝
|
||||
3. 精确 GRANT → 通过
|
||||
4. 通配符 GRANT → 通过
|
||||
5. 无匹配 → 拒绝
|
||||
"""
|
||||
try:
|
||||
grants, denies = await self._getUserPermissions(UserId)
|
||||
|
||||
# DENY 优先
|
||||
if PermissionKey in denies:
|
||||
logger.debug(f"[DENY] 精确拒绝: user={UserId}, perm={PermissionKey}")
|
||||
return False
|
||||
|
||||
if self._matchWildcard(PermissionKey, denies):
|
||||
logger.debug(f"[DENY] 通配符拒绝: user={UserId}, perm={PermissionKey}")
|
||||
return False
|
||||
|
||||
# GRANT
|
||||
if PermissionKey in grants:
|
||||
logger.debug(f"[GRANT] 精确授权: user={UserId}, perm={PermissionKey}")
|
||||
return True
|
||||
|
||||
if self._matchWildcard(PermissionKey, grants):
|
||||
logger.debug(f"[GRANT] 通配符授权: user={UserId}, perm={PermissionKey}")
|
||||
return True
|
||||
|
||||
logger.debug(f"[DENY] 无匹配权限: user={UserId}, perm={PermissionKey}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"权限检查异常: user={UserId}, perm={PermissionKey}, error={e}")
|
||||
return False # 安全优先:异常时拒绝
|
||||
|
||||
async def HasAnyPermission(self, UserId: int, PermissionKeys: list[str]) -> bool:
|
||||
"""OR 逻辑:任一权限通过即返回 True。"""
|
||||
for key in PermissionKeys:
|
||||
if await self.CheckPermission(UserId, key):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def HasAllPermissions(self, UserId: int, PermissionKeys: list[str]) -> bool:
|
||||
"""AND 逻辑:全部权限通过才返回 True。"""
|
||||
for key in PermissionKeys:
|
||||
if not await self.CheckPermission(UserId, key):
|
||||
return False
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 内部方法
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
async def _getUserPermissions(self, UserId: int) -> tuple[set[str], set[str]]:
|
||||
"""从数据库查询用户的 GRANT 和 DENY 权限集合。"""
|
||||
grants: set[str] = set()
|
||||
denies: set[str] = set()
|
||||
|
||||
async with GetAsyncSession() as session:
|
||||
from sqlalchemy import text
|
||||
|
||||
result = await session.execute(
|
||||
text(
|
||||
"SELECT p.permission_key, rp.grant_type "
|
||||
"FROM sso_users u "
|
||||
"JOIN user_role ur ON u.id = ur.user_id "
|
||||
"JOIN roles r ON ur.role_id = r.id "
|
||||
"JOIN role_permissions rp ON r.id = rp.role_id "
|
||||
"JOIN permissions p ON rp.permission_id = p.id "
|
||||
"WHERE u.id = :uid"
|
||||
),
|
||||
{"uid": UserId},
|
||||
)
|
||||
|
||||
rows = result.fetchall()
|
||||
if not rows:
|
||||
logger.warning(f"用户无角色或权限: user_id={UserId}")
|
||||
return grants, denies
|
||||
|
||||
for row in rows:
|
||||
permKey = row[0]
|
||||
grantType = row[1]
|
||||
if grantType == "GRANT":
|
||||
grants.add(permKey)
|
||||
elif grantType == "DENY":
|
||||
denies.add(permKey)
|
||||
|
||||
logger.debug(f"用户权限: user={UserId}, grants={len(grants)}, denies={len(denies)}")
|
||||
return grants, denies
|
||||
|
||||
@staticmethod
|
||||
def _matchWildcard(PermissionKey: str, PermissionSet: set[str]) -> bool:
|
||||
"""检查 PermissionKey 是否匹配集合中的任一通配符模式。"""
|
||||
for pattern in PermissionSet:
|
||||
if PermissionServiceImpl._wildcardMatch(PermissionKey, pattern):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _wildcardMatch(PermissionKey: str, Pattern: str) -> bool:
|
||||
"""通配符匹配。
|
||||
|
||||
_wildcardMatch("document:read:all", "document:*:*") → True
|
||||
_wildcardMatch("document:read:all", "document:read:*") → True
|
||||
_wildcardMatch("document:read:all", "evaluation:*:*") → False
|
||||
_wildcardMatch("document:read:all", "*:*:*") → True
|
||||
"""
|
||||
keyParts = PermissionKey.split(":")
|
||||
patternParts = Pattern.split(":")
|
||||
|
||||
if len(keyParts) != len(patternParts):
|
||||
return False
|
||||
|
||||
for keyPart, patternPart in zip(keyParts, patternParts):
|
||||
if patternPart == "*":
|
||||
continue
|
||||
if keyPart != patternPart:
|
||||
return False
|
||||
|
||||
return True
|
||||
Reference in New Issue
Block a user