fix(server): 连接删掉账户级 dp_uuid 回退,未注册设备直接拒(#16)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Has been cancelled
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Has been cancelled
ci-pangolin / Go — build + test (push) Has been cancelled
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Has been cancelled
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Has been cancelled
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Has been cancelled

漏洞:设备被移除后行已删,但靠残留 access token + 连接时回退「账户级 dp_uuid」
还能连上(EnsureDeviceDpUUID 找不到设备→回退),且不计入活跃设备数,绕过设备上限。

修:ConnectNode 去掉账户级回退。设备未注册/已被移除(EnsureDeviceDpUUID 返回
ErrDeviceNotFound)→ 403 {code:DEVICE_NOT_REGISTERED} + 提示「请重新登录以重新
注册设备」。客户端现有 ConnectApiException 处理会把该 message_zh 显示在连接页,
用户重新登录即重新注册(并命中登录挡板/超限流程)。

- nodes: 新增哨兵 ErrDeviceNotFound;EnsureDeviceDpUUID 包装它
- httpapi ConnectNode: errors.Is(ErrDeviceNotFound) → 拒连;其余错误 500

go build/vet 干净;nodes/httpapi 测试全过。客户端不用改。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-01 22:51:12 +08:00
parent 6ea2ec4a32
commit 0bedd1c4d1
2 changed files with 26 additions and 10 deletions
+6 -1
View File
@@ -3,6 +3,7 @@ package nodes
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
@@ -11,6 +12,10 @@ import (
agentv1 "github.com/wangjia/pangolin/server/internal/pb/agentv1"
)
// ErrDeviceNotFound: connect 时 device_id 不是该用户的已注册设备(未注册或已被移除)。
// connect 据此拒连并提示重新登录(重新注册设备),不再回退账户级 dp_uuid。
var ErrDeviceNotFound = errors.New("device not registered")
// NodeRow holds a node's essential fields from the nodes table.
type NodeRow struct {
ID int64
@@ -375,7 +380,7 @@ func (s *SQLNodeStore) EnsureDeviceDpUUID(ctx context.Context, userID int64, dev
userID, deviceUUID,
).Scan(&deviceID, &dpUUID)
if err == sql.ErrNoRows {
return "", 0, fmt.Errorf("nodes.SQLNodeStore.EnsureDeviceDpUUID: device %q not found for user %d", deviceUUID, userID)
return "", 0, fmt.Errorf("nodes.SQLNodeStore.EnsureDeviceDpUUID: device %q for user %d: %w", deviceUUID, userID, ErrDeviceNotFound)
}
if err != nil {
return "", 0, fmt.Errorf("nodes.SQLNodeStore.EnsureDeviceDpUUID: %w", err)