这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
66 lines
2.8 KiB
Python
66 lines
2.8 KiB
Python
"""Exec approval for Synology Chat channel.
|
|
|
|
Provides approval workflow for privileged operations in the Synology Chat context.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from typing import Any
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
class ApprovalManager:
|
|
def __init__(self, config: dict[str, Any]):
|
|
self._config = config
|
|
self._approvers: list[str] = config.get("security", {}).get("approvers", [])
|
|
self._pending_approvals: dict[str, dict[str, Any]] = {}
|
|
|
|
def get_approvers(self) -> list[str]:
|
|
return list(self._approvers)
|
|
|
|
def is_approver(self, user_id: str) -> bool:
|
|
return user_id in self._approvers
|
|
|
|
def request_approval(
|
|
self,
|
|
action: str,
|
|
requester_id: str,
|
|
details: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
request_id = f"approval:{action}:{requester_id}:{uuid.uuid4().hex[:12]}"
|
|
self._pending_approvals[request_id] = {
|
|
"request_id": request_id,
|
|
"action": action,
|
|
"requester_id": requester_id,
|
|
"status": "pending",
|
|
"details": details or {},
|
|
}
|
|
logger.info(f"[SynologyChat] Approval requested: {action} by {requester_id}")
|
|
return {"request_id": request_id, "status": "pending"}
|
|
|
|
def approve(self, request_id: str, approver_id: str) -> dict[str, Any]:
|
|
if not self.is_approver(approver_id):
|
|
return {"status": "error", "message": f"User {approver_id} is not an approver"}
|
|
if request_id not in self._pending_approvals:
|
|
return {"status": "error", "message": f"Approval request {request_id} not found"}
|
|
self._pending_approvals[request_id]["status"] = "approved"
|
|
self._pending_approvals[request_id]["approved_by"] = approver_id
|
|
logger.info(f"[SynologyChat] Approval {request_id} approved by {approver_id}")
|
|
return {"request_id": request_id, "status": "approved"}
|
|
|
|
def deny(self, request_id: str, approver_id: str, reason: str = "") -> dict[str, Any]:
|
|
if not self.is_approver(approver_id):
|
|
return {"status": "error", "message": f"User {approver_id} is not an approver"}
|
|
if request_id not in self._pending_approvals:
|
|
return {"status": "error", "message": f"Approval request {request_id} not found"}
|
|
self._pending_approvals[request_id]["status"] = "denied"
|
|
self._pending_approvals[request_id]["denied_by"] = approver_id
|
|
self._pending_approvals[request_id]["deny_reason"] = reason
|
|
logger.info(f"[SynologyChat] Approval {request_id} denied by {approver_id}: {reason}")
|
|
return {"request_id": request_id, "status": "denied"}
|
|
|
|
def get_pending(self) -> list[dict[str, Any]]:
|
|
return [r for r in self._pending_approvals.values() if r["status"] == "pending"]
|