verify-auth-permissions.py 4.04 KB
#!/usr/bin/env python3
"""WeChat Viewer 注册/权限分层 ad-hoc 验证(幂等,可重复运行)。

覆盖:注册 → user 登录 → 日报可见(/reports /digest /api/reports GET)→
敏感页 403(/resumes /boss-jobs /chat/* /api/articles 带 CF header)→
隧道流量 API 302/403 → 本地 cron 豁免 200 → admin 全权限。
验证脚本结尾自动清理测试账号 verifyuser。

用法:
    python3 verify-auth-permissions.py
    # 在 wechat-viewer 目录下直接跑;或任意目录跑(脚本自动 chdir)
"""
import os
import sqlite3
import sys

VIEWER = "/data/data/com.termux/files/home/wechat-viewer"
sys.path.insert(0, VIEWER)
os.chdir(VIEWER)

results = []


def check(name, cond, detail=""):
    results.append((name, bool(cond), detail))


# 清理上次残留的 verifyuser(幂等)
_conn = sqlite3.connect(os.path.join(VIEWER, "circle.db"))
_conn.execute("DELETE FROM users WHERE username='verifyuser'")
_conn.commit()
_conn.close()

import app as viewer_app

client = viewer_app.app.test_client()

# 注册
check("register 页面 200", client.get("/register").status_code == 200)
r = client.post("/register", data={"username": "verifyuser", "password": "verify123", "password2": "verify123"})
check("register 提交 302", r.status_code == 302)

# 未登录:公开路由 /digest/ 免登录(2026-08-13 起公网分享),其余仍受保护
check("未登录 /digest 200", client.get("/digest/2026-08-07").status_code == 200)
check("未登录(带CF) /digest 200", client.get("/digest/2026-08-07", headers={"CF-Connecting-IP": "1.2.3.4"}).status_code == 200)
check("未登录 /reports 302", client.get("/reports").status_code == 302)
check("未登录(带CF) /api/reports 302", client.get("/api/reports", headers={"CF-Connecting-IP": "1.2.3.4"}).status_code == 302)

# user 登录
check("user login 302", client.post("/login", data={"username": "verifyuser", "password": "verify123"}).status_code == 302)

# user 日报可见
check("user /reports 200", client.get("/reports").status_code == 200)
check("user /digest 200", client.get("/digest/2026-08-07").status_code == 200)
check("user /api/reports 200", client.get("/api/reports").status_code == 200)

# user 敏感页 403(真实公网流量必带 CF header;不带 header 的 127.0.0.1 = 本地 cron 豁免场景)
for p in ["/resumes", "/boss-jobs", "/chat/x"]:
    check(f"user {p} 403", client.get(p).status_code == 403)
check("user /api/articles(带CF) 403",
      client.get("/api/articles", headers={"CF-Connecting-IP": "1.2.3.4"}).status_code == 403)

# 隧道流量(remote_addr=127.0.0.1 + CF header):未登录 302,user 403
client.get("/logout")
r = client.get("/api/articles", environ_base={"REMOTE_ADDR": "127.0.0.1"},
               headers={"CF-Connecting-IP": "1.2.3.4"})
check("隧道未登录 /api/articles 302", r.status_code == 302)
client.post("/login", data={"username": "verifyuser", "password": "verify123"})
r = client.get("/api/articles", environ_base={"REMOTE_ADDR": "127.0.0.1"},
               headers={"CF-Connecting-IP": "1.2.3.4"})
check("隧道 user /api/articles 403", r.status_code == 403)

# 本地 cron 豁免(127.0.0.1 无 header)→ 200
client.get("/logout")
r = client.get("/api/articles", environ_base={"REMOTE_ADDR": "127.0.0.1"})
check("本地 cron /api/articles 200", r.status_code == 200)

# admin 权限(默认账号 admin/wechat2026)
check("admin login 302", client.post("/login", data={"username": "admin", "password": "wechat2026"}).status_code == 302)
check("admin /resumes 200", client.get("/resumes").status_code == 200)
check("admin /api/articles 200", client.get("/api/articles").status_code == 200)

# 清理测试账号
_conn = sqlite3.connect(os.path.join(VIEWER, "circle.db"))
_conn.execute("DELETE FROM users WHERE username='verifyuser'")
_conn.commit()
_conn.close()

ok = True
for name, cond, detail in results:
    print(("PASS " if cond else "FAIL ") + name + (f" | {detail}" if detail and not cond else ""))
    ok = ok and cond
print()
print("RESULT:", "ALL PASS" if ok else f"{sum(1 for _, c, _ in results if not c)} FAILURES")
sys.exit(0 if ok else 1)