diff --git a/skill/SKILL.md b/skill/SKILL.md new file mode 100644 index 0000000..dcff642 --- /dev/null +++ b/skill/SKILL.md @@ -0,0 +1,48 @@ +--- +name: app_audit +description: 全局审计日志模块。append-only 审计,owner.audit 角色隔离,防自删。Use when 写入/查询/备份/删除审计日志,或为关键操作接入审计。 +--- + +# app_audit — 审计日志全局模块 + +## 架构 + +独立仓库 + 独立 Python 包。其他模块通过 `from app_audit import audit_log` 在关键操作时写入审计。 + +``` +app_audit/ +├── app_audit/ +│ ├── __init__.py # 导出 audit_log / is_audit_role / list/backup/delete +│ ├── audit_service.py # 核心逻辑(纯函数,只依赖 sqlor/appPublic) +│ └── init.py # load_app_audit:add_startup 建表 + owner.audit 角色 +├── models/sd_audit_logs.json +├── wwwroot/index.ui # DataGrid 审计日志列表界面 +├── wwwroot/api/audit.dspy # list/backup/delete +└── scripts/load_path.py # RBAC 权限注册(仅 owner.audit) +``` + +## 数据模型 + +`sd_audit_logs`:id, user_id, username, action, target, detail(text), result, client_ip, created_at。append-only(只 INSERT 不 UPDATE)。 + +## 关键端点 + +`/app_audit/api/audit.dspy`(action 参数): +- `list`:分页查询,返回 `{rows, total}`(DataGrid 兼容)。过滤参数 user_id/username/act/from/to,分页 page/rows。 +- `backup`:导出 JSON,支持过滤 + limit(上限 50000)。 +- `delete`:按 `before` 时间戳删除(`audit_delete` 留痕永不删)。 + +## 审计事件清单(VALID_ACTIONS 白名单) + +认证 login/login_fail/logout;权限 role_change/perm_change/user_role_change;工作环境 work_env_set/org_key_gen/remote_bwrap;部署账号 account_create/account_remove/sandbox_run;用户机构 user_create/user_disable/user_delete/org_change;审计自身 audit_delete/audit_backup。非白名单 action 归 `unknown`(detail 保留原始值)。 + +## Pitfalls + +- **审计独立性**:owner.audit 与 owner.superuser 完全隔离,superuser 也无权看审计。双层校验(RBAC path 挂 owner.audit + audit.dspy 内 is_audit_role),应用层是兜底(独立 app 的 RBAC 对未注册路径可能放行)。 +- **审计写入是旁路**:audit_log 内 try/except,失败不阻断主流程。 +- **run 类接口 result 判断用 `rc == 0`**:run_in_sandbox/run_in_work_env 返回 `{rc, stdout, stderr}`,没有 `ok` 键,不能 `r.get('ok')`(会恒判 fail)。 +- **sqlor `%` 是占位符**:LIKE 要写 `%%`。 +- **INSERT IGNORE 在 aiomysql 打 Duplicate entry 警告**(无害噪音)。 +- **RBAC 缓存 600s TTL**:注册新权限后要重启服务才生效。 +- **审计员用户 user_status='0' 才是启用**:basic_auth 判断 `user_status != '0'` 为禁用。 +- **owner.audit 角色的 role 表 id 是 'owner.audit' 字符串**(非随机 ID),userrole.roleid 直接存 'owner.audit'。