""" 权限管理API 支持RBAC(基于角色的访问控制) """ from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from sqlalchemy import or_ from pydantic import BaseModel from typing import List, Optional import logging from app.core.database import get_db from app.models.permission import Role, Permission, WorkflowPermission, AgentPermission from app.models.user import User from app.models.workflow import Workflow from app.models.agent import Agent from app.api.auth import get_current_user from app.core.exceptions import NotFoundError, ConflictError, ValidationError logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/permissions", tags=["permissions"]) # ========== 角色管理 ========== class RoleCreate(BaseModel): """角色创建模型""" name: str description: Optional[str] = None permission_ids: Optional[List[str]] = None class RoleUpdate(BaseModel): """角色更新模型""" name: Optional[str] = None description: Optional[str] = None permission_ids: Optional[List[str]] = None class RoleResponse(BaseModel): """角色响应模型""" id: str name: str description: Optional[str] is_system: bool permissions: List[dict] user_count: int created_at: str updated_at: str class Config: from_attributes = True @router.get("/roles", response_model=List[RoleResponse]) async def get_roles( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取角色列表(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可访问") roles = db.query(Role).offset(skip).limit(limit).all() result = [] for role in roles: result.append({ "id": role.id, "name": role.name, "description": role.description, "is_system": role.is_system, "permissions": [{"id": p.id, "name": p.name, "code": p.code} for p in role.permissions], "user_count": len(role.users.all()) if hasattr(role.users, 'all') else 0, "created_at": role.created_at.isoformat() if role.created_at else None, "updated_at": role.updated_at.isoformat() if role.updated_at else None }) return result @router.post("/roles", response_model=RoleResponse) async def create_role( role_data: RoleCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """创建角色(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可创建角色") # 检查角色名称是否已存在 existing = db.query(Role).filter(Role.name == role_data.name).first() if existing: raise ConflictError(f"角色名称 '{role_data.name}' 已存在") # 创建角色 role = Role( name=role_data.name, description=role_data.description ) db.add(role) # 分配权限 if role_data.permission_ids: permissions = db.query(Permission).filter(Permission.id.in_(role_data.permission_ids)).all() role.permissions = permissions db.commit() db.refresh(role) return { "id": role.id, "name": role.name, "description": role.description, "is_system": role.is_system, "permissions": [{"id": p.id, "name": p.name, "code": p.code} for p in role.permissions], "user_count": 0, "created_at": role.created_at.isoformat() if role.created_at else None, "updated_at": role.updated_at.isoformat() if role.updated_at else None } @router.put("/roles/{role_id}", response_model=RoleResponse) async def update_role( role_id: str, role_data: RoleUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """更新角色(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可更新角色") role = db.query(Role).filter(Role.id == role_id).first() if not role: raise NotFoundError("角色", role_id) if role.is_system: raise HTTPException(status_code=400, detail="系统角色不可修改") # 更新字段 if role_data.name is not None: # 检查名称是否重复 existing = db.query(Role).filter(Role.name == role_data.name, Role.id != role_id).first() if existing: raise ConflictError(f"角色名称 '{role_data.name}' 已存在") role.name = role_data.name if role_data.description is not None: role.description = role_data.description # 更新权限 if role_data.permission_ids is not None: permissions = db.query(Permission).filter(Permission.id.in_(role_data.permission_ids)).all() role.permissions = permissions db.commit() db.refresh(role) return { "id": role.id, "name": role.name, "description": role.description, "is_system": role.is_system, "permissions": [{"id": p.id, "name": p.name, "code": p.code} for p in role.permissions], "user_count": len(role.users.all()) if hasattr(role.users, 'all') else 0, "created_at": role.created_at.isoformat() if role.created_at else None, "updated_at": role.updated_at.isoformat() if role.updated_at else None } @router.delete("/roles/{role_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_role( role_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """删除角色(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可删除角色") role = db.query(Role).filter(Role.id == role_id).first() if not role: raise NotFoundError("角色", role_id) if role.is_system: raise HTTPException(status_code=400, detail="系统角色不可删除") db.delete(role) db.commit() # ========== 权限管理 ========== class PermissionResponse(BaseModel): """权限响应模型""" id: str name: str code: str resource: str action: str description: Optional[str] created_at: str updated_at: str class Config: from_attributes = True @router.get("/permissions", response_model=List[PermissionResponse]) async def get_permissions( resource: Optional[str] = None, action: Optional[str] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取权限列表""" query = db.query(Permission) if resource: query = query.filter(Permission.resource == resource) if action: query = query.filter(Permission.action == action) permissions = query.all() return [ { "id": p.id, "name": p.name, "code": p.code, "resource": p.resource, "action": p.action, "description": p.description, "created_at": p.created_at.isoformat() if p.created_at else None, "updated_at": p.updated_at.isoformat() if p.updated_at else None } for p in permissions ] # ========== 用户角色管理 ========== class UserRoleAssign(BaseModel): """用户角色分配模型""" user_id: str role_ids: List[str] @router.post("/users/{user_id}/roles") async def assign_user_roles( user_id: str, role_ids: List[str], db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """为用户分配角色(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可分配角色") user = db.query(User).filter(User.id == user_id).first() if not user: raise NotFoundError("用户", user_id) roles = db.query(Role).filter(Role.id.in_(role_ids)).all() if len(roles) != len(role_ids): raise NotFoundError("角色", "部分角色不存在") user.roles = roles db.commit() return { "user_id": user_id, "roles": [{"id": r.id, "name": r.name} for r in roles] } @router.get("/users", response_model=List[dict]) async def get_users( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取用户列表(仅管理员)""" if current_user.role != "admin": raise HTTPException(status_code=403, detail="仅管理员可访问") users = db.query(User).offset(skip).limit(limit).all() result = [] for user in users: # 获取用户的角色 roles = user.roles.all() if hasattr(user.roles, 'all') else list(user.roles) result.append({ "id": user.id, "username": user.username, "email": user.email, "role": user.role, "roles": [{"id": r.id, "name": r.name, "description": r.description} for r in roles], "created_at": user.created_at.isoformat() if user.created_at else None }) return result @router.get("/users/{user_id}/roles") async def get_user_roles( user_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取用户的角色列表""" # 用户只能查看自己的角色,管理员可以查看所有用户的角色 if current_user.id != user_id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权访问") user = db.query(User).filter(User.id == user_id).first() if not user: raise NotFoundError("用户", user_id) roles = user.roles.all() if hasattr(user.roles, 'all') else list(user.roles) return { "user_id": user_id, "roles": [{"id": r.id, "name": r.name, "description": r.description} for r in roles] } # ========== 工作流权限管理 ========== class WorkflowPermissionCreate(BaseModel): """工作流权限创建模型""" workflow_id: str user_id: Optional[str] = None role_id: Optional[str] = None permission_type: str # read/write/execute/share @router.post("/workflows/{workflow_id}/permissions") async def grant_workflow_permission( workflow_id: str, permission_data: WorkflowPermissionCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """授予工作流权限""" workflow = db.query(Workflow).filter(Workflow.id == workflow_id).first() if not workflow: raise NotFoundError("工作流", workflow_id) # 只有工作流所有者或管理员可以授权 if workflow.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权授权此工作流") # 验证权限类型 if permission_data.permission_type not in ["read", "write", "execute", "share"]: raise ValidationError("权限类型必须是: read/write/execute/share") # user_id和role_id必须至少有一个 if not permission_data.user_id and not permission_data.role_id: raise ValidationError("必须指定user_id或role_id") # 检查权限是否已存在 existing = db.query(WorkflowPermission).filter( WorkflowPermission.workflow_id == workflow_id, WorkflowPermission.user_id == permission_data.user_id, WorkflowPermission.role_id == permission_data.role_id, WorkflowPermission.permission_type == permission_data.permission_type ).first() if existing: raise ConflictError("权限已存在") # 创建权限 permission = WorkflowPermission( workflow_id=workflow_id, user_id=permission_data.user_id, role_id=permission_data.role_id, permission_type=permission_data.permission_type, granted_by=current_user.id ) db.add(permission) db.commit() db.refresh(permission) return { "id": permission.id, "workflow_id": workflow_id, "user_id": permission_data.user_id, "role_id": permission_data.role_id, "permission_type": permission_data.permission_type, "granted_by": current_user.id } @router.get("/workflows/{workflow_id}/permissions") async def get_workflow_permissions( workflow_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取工作流权限列表""" workflow = db.query(Workflow).filter(Workflow.id == workflow_id).first() if not workflow: raise NotFoundError("工作流", workflow_id) # 只有工作流所有者或管理员可以查看权限 if workflow.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权查看此工作流的权限") permissions = db.query(WorkflowPermission).filter( WorkflowPermission.workflow_id == workflow_id ).all() return [ { "id": p.id, "user_id": p.user_id, "role_id": p.role_id, "permission_type": p.permission_type, "granted_by": p.granted_by, "created_at": p.created_at.isoformat() if p.created_at else None } for p in permissions ] @router.delete("/workflows/{workflow_id}/permissions/{permission_id}", status_code=status.HTTP_204_NO_CONTENT) async def revoke_workflow_permission( workflow_id: str, permission_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """撤销工作流权限""" workflow = db.query(Workflow).filter(Workflow.id == workflow_id).first() if not workflow: raise NotFoundError("工作流", workflow_id) permission = db.query(WorkflowPermission).filter( WorkflowPermission.id == permission_id, WorkflowPermission.workflow_id == workflow_id ).first() if not permission: raise NotFoundError("权限", permission_id) # 只有工作流所有者、管理员或授权人可以撤销 if (workflow.user_id != current_user.id and current_user.role != "admin" and permission.granted_by != current_user.id): raise HTTPException(status_code=403, detail="无权撤销此权限") db.delete(permission) db.commit() # ========== Agent权限管理 ========== class AgentPermissionCreate(BaseModel): """Agent权限创建模型""" agent_id: str user_id: Optional[str] = None role_id: Optional[str] = None permission_type: str # read/write/execute/deploy @router.post("/agents/{agent_id}/permissions") async def grant_agent_permission( agent_id: str, permission_data: AgentPermissionCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """授予Agent权限""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError("Agent", agent_id) # 只有Agent所有者或管理员可以授权 if agent.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权授权此Agent") # 验证权限类型 if permission_data.permission_type not in ["read", "write", "execute", "deploy"]: raise ValidationError("权限类型必须是: read/write/execute/deploy") # user_id和role_id必须至少有一个 if not permission_data.user_id and not permission_data.role_id: raise ValidationError("必须指定user_id或role_id") # 检查权限是否已存在 existing = db.query(AgentPermission).filter( AgentPermission.agent_id == agent_id, AgentPermission.user_id == permission_data.user_id, AgentPermission.role_id == permission_data.role_id, AgentPermission.permission_type == permission_data.permission_type ).first() if existing: raise ConflictError("权限已存在") # 创建权限 permission = AgentPermission( agent_id=agent_id, user_id=permission_data.user_id, role_id=permission_data.role_id, permission_type=permission_data.permission_type, granted_by=current_user.id ) db.add(permission) db.commit() db.refresh(permission) return { "id": permission.id, "agent_id": agent_id, "user_id": permission_data.user_id, "role_id": permission_data.role_id, "permission_type": permission_data.permission_type, "granted_by": current_user.id } @router.get("/agents/{agent_id}/permissions") async def get_agent_permissions( agent_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """获取Agent权限列表""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError("Agent", agent_id) # 只有Agent所有者或管理员可以查看权限 if agent.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权查看此Agent的权限") permissions = db.query(AgentPermission).filter( AgentPermission.agent_id == agent_id ).all() return [ { "id": p.id, "user_id": p.user_id, "role_id": p.role_id, "permission_type": p.permission_type, "granted_by": p.granted_by, "created_at": p.created_at.isoformat() if p.created_at else None } for p in permissions ] @router.delete("/agents/{agent_id}/permissions/{permission_id}", status_code=status.HTTP_204_NO_CONTENT) async def revoke_agent_permission( agent_id: str, permission_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """撤销Agent权限""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError("Agent", agent_id) permission = db.query(AgentPermission).filter( AgentPermission.id == permission_id, AgentPermission.agent_id == agent_id ).first() if not permission: raise NotFoundError("权限", permission_id) # 只有Agent所有者、管理员或授权人可以撤销 if (agent.user_id != current_user.id and current_user.role != "admin" and permission.granted_by != current_user.id): raise HTTPException(status_code=403, detail="无权撤销此权限") db.delete(permission) db.commit()