590 lines
18 KiB
Python
590 lines
18 KiB
Python
"""
|
||
权限管理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()
|