Quellcode durchsuchen

docs: lj-test 权限对齐自查指令

用户裁决:同公司成员必须能上传,不区分 owner。

云函数 lfYlgU7SkK 的 pendingArtifacts 已改为「owner 或同公司」,
但容器侧若有自己的 owner 校验仍会挡住,需自查消除。

Co-Authored-By: Claude Code <noreply@anthropic.com>
Huccct vor 13 Stunden
Ursprung
Commit
f9935ce10b

+ 274 - 0
docs/03-interface/ljtest-权限对齐-回答.md

@@ -0,0 +1,274 @@
+# 容器取证问答:agent-node-lj-test
+
+- 时间:2026-11-14T05:30:35.756Z
+- 房间:`!HuxdokMHYR9AFDXUapXt_JKZVO1bFMzok6PRuFnQ9Ts`(临时房间,可保留查阅)
+- 消息统计:正文 13 段 / 工具过程 1 段 / 文件 0 个
+- 结束原因:收到 ✅ reaction
+
+## 我发出的问题
+
+```text
+# 用户裁决:同公司成员必须能上传,不能只限 owner
+
+## 背景
+
+云函数 `lfYlgU7SkK` 的 `pendingArtifacts` 权限**已由我这边改成「owner 或同公司」**(2026-11-14 部署验证)。
+
+改前改后实测:
+```
+ksftest01(非 owner,同公司)
+改前:{"code":403,"msg":"仅该 agent 的 owner 可领取任务"}
+改后:{"code":200,"jobs":21}
+```
+
+**用户明确要求:不区分 owner,只要是同公司就能传。**
+
+---
+
+# 要你做的:自查并消除容器侧的 owner 限制
+
+云函数已经放开了,**但容器侧如果有自己的 owner 校验,照样会挡住同公司用户**。
+
+## 1. 查你自己有没有做 owner 校验
+
+```bash
+echo "=== 搬运链路里有没有 owner / 权限判断 ==="
+grep -rn "owner\|isOwner\|permission\|无权\|403\|Forbidden" \
+  /opt/data/scripts/fmode-artifact-push.py \
+  /opt/data/git-repos/agent-status-reporter/scripts/artifact-drain.py \
+  /opt/data/scripts/fmode-deliverable-hook.py 2>/dev/null | head -30
+
+echo "=== 回调/上报链路 ==="
+grep -rn "owner\|ownerId" /opt/data/scripts/ /opt/data/plugins/ 2>/dev/null | grep -v ".pyc" | head -20
+```
+
+## 2. 查 agent 归属是怎么确定的
+
+```bash
+echo "=== AGENT_ID 从哪来 ==="
+grep -rn "AGENT_ID\|agentId" /opt/data/.env /opt/data/config.yaml 2>/dev/null | head -10
+
+echo "=== registerFile 传的 agentId ==="
+grep -rn "registerFile" -A 12 /opt/data/scripts/fmode-deliverable-hook.py 2>/dev/null | head -25
+```
+
+**要回答**:
+- 你调云函数时用的 token 是**容器的**还是**用户的**?
+- 如果是容器的:容器是不是这个 agent 的 owner?(是的话 `pendingArtifacts` 本来就能过)
+- 如果是用户的:那个用户是谁?(决定会不会被 403)
+
+## 3. 实测:非 owner 用户走完整流程
+
+**这是最关键的一步。** 用**非 owner 的同公司账号**真的跑一次。
+
+先确认你手上有哪些账号可用:
+
+```bash
+echo "=== 容器自己是谁 ==="
+python3 - <<'PY'
+import json, os, urllib.request
+API="https://server.fmode.cn/api/functions"
+TOK=os.environ.get("FMODE_SESSION_TOKEN") or ""
+def call(fn,p):
+    req=urllib.request.Request(API,data=json.dumps({"id":fn,"token":TOK,"params":p}).encode(),
+        headers={"Content-Type":"application/json","X-Parse-Application-Id":"ncloudmaster"},method="POST")
+    return json.loads(urllib.request.urlopen(req,timeout=30).read())
+r=call("lfYlgU7SkK",{"action":"pendingArtifacts","agentId":os.environ.get("AGENT_ID","")})
+print("pendingArtifacts =", json.dumps(r, ensure_ascii=False)[:300])
+PY
+```
+
+**如果这里 403,说明容器用的 token 不是这个 agent 的 owner —— 那正好是本次要解决的问题,报给我。**
+
+## 4. 确认改动后,同公司用户能完整走通
+
+如果你能拿到一个非 owner 的同公司用户 token,请实测这条链:
+
+```
+用该用户 token 调 pendingArtifacts
+→ 应该 200,能拿到 jobs
+```
+
+**拿不到就如实说「没有非 owner 账号可测」,不要编造。**
+
+---
+
+# 回给我的格式
+
+```
+## 1. 容器侧有没有 owner 校验
+grep 结果 = ?
+有 / 没有
+
+## 2. agent 归属
+容器调的 token 是谁的 = ?
+是不是 owner = ?
+
+## 3. 实测
+改前 pendingArtifacts = ?
+改后 = ?
+(如果用容器自己的 token 测,说明清楚)
+
+## 4. 非 owner 测试
+有没有非 owner 账号 = ?
+能测吗 = ?
+结果 = ?
+
+## 5. 结论
+容器侧还需要改吗 = ?
+```
+
+🔴 **只查不改,先把现状给我。**
+```
+
+## 容器回答(原文,按时间顺序,未删改)
+
+### 1【prose】
+
+```text
+我先按您给的命令逐条查现状,只读不改。先看权限判断和归属来源。
+```
+
+### 2【prose】
+
+```text
+terminal · echo "=== 搬运链路里有没有 owner / 权限判断 ===" grep -rn "owner\|isOwne…
+```
+
+### 3【prose】
+
+```text
+已收到,正在查:搬运链路的 grep 已出初步结果(没有 owner 准入校验),接下来确认 token 归属并实测。
+```
+
+### 4【prose】
+
+```text
+```
+echo "=== 回调/上报链路 owner ===" ...
+```
+```
+echo "=== registerFile 传参 ===" ...
+```
+```
+
+### 5【prose】
+
+```text
+grep 已看完,容器侧没有 owner 准入校验。现在做最关键的实测:用容器 token 调 pendingArtifacts。
+```
+
+### 6【prose】
+
+```text
+```
+echo "=== token 来源(只显示前12位) ===" ...
+```
+```
+echo "=== 容器自己是谁 ===" ...
+```
+```
+
+### 7【prose】
+
+```text
+实测返回 200,但 jobs 为空。接下来先查清容器 token 对应的真实用户身份,再判断第 4 步是否有可用账号。
+```
+
+### 8【prose】
+
+```text
+```
+echo "=== _resolve_agent_id 实现 ===" ...
+```
+```
+echo "=== 容器 token 对应的用户身份 ===" ...
+```
+```
+
+### 9【prose】
+
+```text
+容器 token 在 api 网关上是 401(说明它是云函数的专用 session,而不是普通用户的 API token)。接下来继续确认它到底是谁,以及是否存在其他账号。
+```
+
+### 10【prose】
+
+```text
+```
+echo "=== 有没有其它用户/账号凭据落盘 ===" ...
+```
+```
+echo "=== 同一个 token 换云函数网关探身份 ===" ...
+```
+```
+
+### 11【prose】
+
+```text
+最后确认一件事:容器里有没有任何第二个用户的凭据(这决定第 4 步能不能测)。
+```
+
+### 12【prose】
+
+```text
+```
+echo "=== 是否有别的 session/用户 token 落盘(模...
+```
+```
+echo "=== 全盘找非本机 session token(ksftes...
+```
+```
+
+### 13【prose】
+
+```text
+查完了,下面按您要的格式回报。**没有做任何修改。**
+
+## 1. 容器侧有没有 owner 校验
+grep 结果 = 三个搬运/登记脚本里**没有任何准入判断**:
+- `fmode-artifact-push.py`:2 处 `403` 命中,都在注释里,讲的是 OBS SigV2 对中文文件名的 `SignatureDoesNotMatch`,跟权限无关
+- `fmode-deliverable-hook.py`:`owner` 命中 4 处(619/620/621/622 行),那是循环变量 `owner = next(s for s in ck ...)`,只是「本地缓存目录名」,纯字符串命名,不是身份
+- `artifact-drain.py`:0 命中
+
+判 owner 的分支 = **没有**(`if .*(owner|ownerId|userId|isOwner)` 在三文件里零命中)
+
+## 2. agent 归属
+容器调的 token 是谁的 = 容器自己的 `FMODE_SESSION_TOKEN`(`/opt/data/.env:560` + s6 container_environment 注入,len=34,前缀 `r:dd15e8993d…`)。这个 token 是**容器/云函数侧 session,不是用户 API token** —— 拿它去 `api.fmode.cn/api/user/me|self|info` 三个端点全部 `401 AUTH_UNAUTHORIZED invalid access token`。
+
+是不是 owner = **从容器侧无法自证**。容器没有能读「我是谁/谁是 owner」的接口,云函数也没回 owner 字段。只能反推:它带 `agentId=agent-node-lj-test` 调 `pendingArtifacts` 得到 200,说明**这个 token 至少对该 agent 有权限**(改前若它是非 owner 且云函数还是旧逻辑,应该拿到 403)。
+
+## 3. 实测
+改前 pendingArtifacts = 未观察到 403。容器当前实测(改后云函数):
+```
+agentId='agent-node-lj-test' -> {"code": 200, "jobs": []}
+jobs count = 0
+```
+agentId 传空时 `{"code":400,"msg":"agentId required"}` —— 参数校验正常工作,不是权限拦截。
+
+说明:这是**用容器自己的 token 测的**,且 queue 是空的(jobs=[] 不能证明权限放行,只能证明没被 403 挡)。
+
+## 4. 非 owner 测试
+有没有非 owner 账号 = **没有**。`/opt/data/.fmode/` 下只有 active-reporter / topup 的 state 文件,无任何第二用户凭据;`.env` 只有本机 `FMODE_SESSION_TOKEN` 和 `MATRIX_ACCESS_TOKEN`;全盘 grep `ksftest` 零命中。
+
+能测吗 = **不能**,没有 ksftest01 或任何同公司其他成员的 token 可用。
+
+结果 = 无。**我不会编造这个结果** —— 需要您把 ksftest01 的 session token(或一个非 owner 同公司账号的 token)给我,我立刻按 `pendingArtifacts → 200 + jobs` 这条链实测回报。
+
+## 5. 结论
+容器侧还需要改吗 = **不需要**。容器侧三条链路(push / drain / hook)从头到尾没有 owner 校验,只做「拿 agentId + 拿自己的 token → 调云函数」,权限判定 100% 在云函数侧。您那边放开「owner 或同公司」后,容器侧不需要任何同步改动。
+
+**唯一待补的证据**是第 4 步的非 owner 实测 —— 卡在没有账号,不是卡在代码。给我一个非 owner 同公司的 token,我就补上这一格。
+```
+
+### 14【ack】
+
+```text
+✅ 容器已标记完成
+```
+
+---
+
+## 工具过程(仅供核对,非回答内容)
+
+- `💻 terminal ``` echo "=== 搬运链路里有没有 owner / 权限判断 ===" ... ````

+ 111 - 0
docs/03-interface/指令-ljtest-权限对齐.md

@@ -0,0 +1,111 @@
+# 用户裁决:同公司成员必须能上传,不能只限 owner
+
+## 背景
+
+云函数 `lfYlgU7SkK` 的 `pendingArtifacts` 权限**已由我这边改成「owner 或同公司」**(2026-11-14 部署验证)。
+
+改前改后实测:
+```
+ksftest01(非 owner,同公司)
+改前:{"code":403,"msg":"仅该 agent 的 owner 可领取任务"}
+改后:{"code":200,"jobs":21}
+```
+
+**用户明确要求:不区分 owner,只要是同公司就能传。**
+
+---
+
+# 要你做的:自查并消除容器侧的 owner 限制
+
+云函数已经放开了,**但容器侧如果有自己的 owner 校验,照样会挡住同公司用户**。
+
+## 1. 查你自己有没有做 owner 校验
+
+```bash
+echo "=== 搬运链路里有没有 owner / 权限判断 ==="
+grep -rn "owner\|isOwner\|permission\|无权\|403\|Forbidden" \
+  /opt/data/scripts/fmode-artifact-push.py \
+  /opt/data/git-repos/agent-status-reporter/scripts/artifact-drain.py \
+  /opt/data/scripts/fmode-deliverable-hook.py 2>/dev/null | head -30
+
+echo "=== 回调/上报链路 ==="
+grep -rn "owner\|ownerId" /opt/data/scripts/ /opt/data/plugins/ 2>/dev/null | grep -v ".pyc" | head -20
+```
+
+## 2. 查 agent 归属是怎么确定的
+
+```bash
+echo "=== AGENT_ID 从哪来 ==="
+grep -rn "AGENT_ID\|agentId" /opt/data/.env /opt/data/config.yaml 2>/dev/null | head -10
+
+echo "=== registerFile 传的 agentId ==="
+grep -rn "registerFile" -A 12 /opt/data/scripts/fmode-deliverable-hook.py 2>/dev/null | head -25
+```
+
+**要回答**:
+- 你调云函数时用的 token 是**容器的**还是**用户的**?
+- 如果是容器的:容器是不是这个 agent 的 owner?(是的话 `pendingArtifacts` 本来就能过)
+- 如果是用户的:那个用户是谁?(决定会不会被 403)
+
+## 3. 实测:非 owner 用户走完整流程
+
+**这是最关键的一步。** 用**非 owner 的同公司账号**真的跑一次。
+
+先确认你手上有哪些账号可用:
+
+```bash
+echo "=== 容器自己是谁 ==="
+python3 - <<'PY'
+import json, os, urllib.request
+API="https://server.fmode.cn/api/functions"
+TOK=os.environ.get("FMODE_SESSION_TOKEN") or ""
+def call(fn,p):
+    req=urllib.request.Request(API,data=json.dumps({"id":fn,"token":TOK,"params":p}).encode(),
+        headers={"Content-Type":"application/json","X-Parse-Application-Id":"ncloudmaster"},method="POST")
+    return json.loads(urllib.request.urlopen(req,timeout=30).read())
+r=call("lfYlgU7SkK",{"action":"pendingArtifacts","agentId":os.environ.get("AGENT_ID","")})
+print("pendingArtifacts =", json.dumps(r, ensure_ascii=False)[:300])
+PY
+```
+
+**如果这里 403,说明容器用的 token 不是这个 agent 的 owner —— 那正好是本次要解决的问题,报给我。**
+
+## 4. 确认改动后,同公司用户能完整走通
+
+如果你能拿到一个非 owner 的同公司用户 token,请实测这条链:
+
+```
+用该用户 token 调 pendingArtifacts
+→ 应该 200,能拿到 jobs
+```
+
+**拿不到就如实说「没有非 owner 账号可测」,不要编造。**
+
+---
+
+# 回给我的格式
+
+```
+## 1. 容器侧有没有 owner 校验
+grep 结果 = ?
+有 / 没有
+
+## 2. agent 归属
+容器调的 token 是谁的 = ?
+是不是 owner = ?
+
+## 3. 实测
+改前 pendingArtifacts = ?
+改后 = ?
+(如果用容器自己的 token 测,说明清楚)
+
+## 4. 非 owner 测试
+有没有非 owner 账号 = ?
+能测吗 = ?
+结果 = ?
+
+## 5. 结论
+容器侧还需要改吗 = ?
+```
+
+🔴 **只查不改,先把现状给我。**