Files
cma-management/check_cma_detail.py
Hermes CI Fix 40d522cd34 fix(security): retire default-credential surface (yanxue-20260914-cma-defaultcred-code-fix)
- Login.vue: no prefilled default creds; no anonymous entity enumeration on mount

- deploy.sh: readiness polling (0.5s/30s cap) replaces fixed sleep 3; login smoke reads same-source 600 secret file, missing file = explicit abort

- 19 verify/check/gen_report scripts: shared _admin_creds() loader (CMA_ADMIN_ENV -> ops .secrets -> profile copy); no hardcoded creds

- tests: single TEST_USER_PASSWORD fixture constant; docs: pointer to 600 secret file; removed 4 legacy .bak + pycache residue
2026-09-14 17:39:53 +08:00

166 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""管理会计OS数据详情检查"""
import json, urllib.request
import os # noqa: E402 (凭据加载器需要)
# ---- 管理员凭据同源加载(2026-09-14 cma-defaultcred-code-fix)----
# 生产凭据只存 600 密钥文件;禁止在脚本/文档/日志里硬编码口令。
# 顺序:$CMA_ADMIN_ENV → wecom-ops 主副本(ops 轮换落点,权威) → 本 profile 副本。
def _admin_creds():
"""返回 (user, password, 来源文件);未找到 → 显式报错退出(不静默退回默认口令)"""
for p in (os.environ.get("CMA_ADMIN_ENV"),
"/root/.hermes/profiles/wecom-ops/.secrets/cma-admin.env",
"/root/.hermes/profiles/wecom-fullstack/.secrets/cma-admin.env"):
if p and os.path.isfile(p):
kv = {}
for line in open(p, encoding="utf-8"):
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
kv[k.strip()] = v.strip().strip('"').strip("'")
if kv.get("CMA_ADMIN_USER") and kv.get("CMA_ADMIN_PASS"):
return kv["CMA_ADMIN_USER"], kv["CMA_ADMIN_PASS"], p
raise SystemExit("[错误] 未找到 CMA 管理员凭据:请设 $CMA_ADMIN_ENV,或准备 600 权限密钥文件 "
".secrets/cma-admin.env(键名 CMA_ADMIN_USER / CMA_ADMIN_PASS)")
ADMIN_USER, ADMIN_PASS, CRED_SRC = _admin_creds()
print(f"[creds] 来源={CRED_SRC} 账号={ADMIN_USER}(口令不回显)")
BASE = "http://127.0.0.1:8010"
def login():
data = json.dumps({"username":ADMIN_USER,"password":ADMIN_PASS}).encode()
req = urllib.request.Request(BASE + "/api/cma/auth/login", data=data,
headers={"Content-Type":"application/json"}, method="POST")
resp = json.loads(urllib.request.urlopen(req).read())
return resp["token"]
def api(path, token):
auth = "Bearer " + token
req = urllib.request.Request(BASE + path, headers={"Authorization": auth})
resp = json.loads(urllib.request.urlopen(req).read())
return resp
token = login()
# KPI详情
print("="*60)
print("KPI定义(前5条)")
print("="*60)
data = api("/api/cma/kpis", token)
for kpi in data.get("data", data)[:5]:
print(" [%s] %s" % (kpi.get("code","?"), kpi.get("name","?")))
print(" 维度:%s | 权重:%s | 单位:%s" % (
kpi.get("dimension","?"), kpi.get("weight","?"), kpi.get("unit","?")))
print(" 当前值:%s | 目标值:%s | 实际值:%s" % (
kpi.get("current_value","-"), kpi.get("target_value","-"), kpi.get("actual_value","-")))
if kpi.get("threshold_green"):
print(" 阈值: 绿<%s 黄<%s 红<%s" % (
kpi.get("threshold_green",""), kpi.get("threshold_yellow",""), kpi.get("threshold_red","")))
print()
# 用户
print("="*60)
print("用户列表")
print("="*60)
data = api("/api/cma/users", token)
if isinstance(data, dict):
# 可能是 {username: {...}} 格式
users = [data[k] for k in data if isinstance(data[k], dict)]
else:
users = data
for u in users:
print(" %-12s | %-15s | %-10s | role_code=%s" % (
u.get("username","?"), u.get("name","?"), u.get("role_name","?"), u.get("role","?")))
# 战略地图
print()
print("="*60)
print("战略地图")
print("="*60)
data = api("/api/cma/maps", token)
for m in data.get("data", data):
print(" [%s] %s - %s, 状态:%s" % (
m.get("id","?"), m.get("title","?"), m.get("period","?"), m.get("status","?")))
# 数据源
print()
print("="*60)
print("数据源配置")
print("="*60)
data = api("/api/cma/data/sources", token)
for s in data.get("data", data):
print(" %s - 类型:%s, API:%s" % (
s.get("name","?"), s.get("type","?"), s.get("api_endpoint","?")))
# 通知渠道
print()
print("="*60)
print("通知渠道")
print("="*60)
data = api("/api/cma/notifications/channels", token)
for c in data.get("data", data):
print(" %s - %s, 启用:%s" % (
c.get("name","?"), c.get("type","?"), c.get("enabled","?")))
# 预警规则
print()
print("="*60)
print("预警规则(前8条)")
print("="*60)
data = api("/api/cma/alert-rules", token)
rules = data.get("data", data)
print(" 共 %d 条规则" % len(rules))
for r in rules[:8]:
print(" %s - KPI:%s, 条件:%s %s" % (
r.get("name","?"), r.get("kpi_code","?"), r.get("condition","?"), r.get("threshold","?")))
# 预警记录
print()
print("="*60)
print("预警记录(前6条)")
print("="*60)
data = api("/api/cma/alerts", token)
for a in data.get("data", data)[:6]:
print(" [%s] %s" % (a.get("severity","?"), a.get("kpi_name","?")))
print(" 值:%s/%s 阈值:%s 状态:%s 处理人:%s" % (
a.get("current_value","?"), a.get("target_value","?"),
a.get("threshold","?"), a.get("status","?"), a.get("resolver","?")))
# 行动计划
print()
print("="*60)
print("行动计划")
print("="*60)
data = api("/api/cma/action-plans", token)
for p in data.get("data", data):
print(" %s - KPI:%s, 状态:%s, 负责人:%s" % (
p.get("title","?"), p.get("kpi_code","?"), p.get("status","?"), p.get("assignee","?")))
# Dashboard summary
print()
print("="*60)
print("Dashboard Summary")
print("="*60)
data = api("/api/cma/dashboard/summary", token)
print(" KPI总数:%s" % data.get("kpi_total"))
print(" 预警数:%s" % data.get("alert_count"))
ds = data.get("dimension_stats", {})
print(" 维度统计: %s" % json.dumps(ds, ensure_ascii=False))
# 权限
print()
print("="*60)
print("权限配置")
print("="*60)
data = api("/api/cma/permissions/config", token)
rp = data.get("route_permissions", {})
print(" 路由权限(每个角色模块数):")
for role, modules in rp.items():
print(" %-12s: %d 个模块" % (role, len(modules)))
ap = data.get("action_permissions", {})
print(" 操作权限:")
for role, actions in ap.items():
print(" %-12s: %s" % (role, actions))