fix: permission checks for speedId and inPort + multi-node IP constraints (#261)

* feat: restrict user permissions and multi-node IP constraints

- Non-admin users cannot set speedId or inPort on forward create/update
- Multi-entrance tunnels disable custom listen IP for forwards
- Multi-exit tunnels disable custom connect IP
- Multi-node hop chains disable custom connect IP per hop
- Remove tunnel-first-IP fallback in forward ingress resolution
- Add contract tests for non-admin permission restrictions

Entire-Checkpoint: 133693290660

* fix: allow non-admin users to submit null speedId and zero inPort

- Backend: Check speedId is not nil before rejecting non-admin requests
- Backend: Only reject inPort if value > 0 for non-admin users
- Frontend: Only include speedId and inPort in payload for admin users
- Tests: Add contract tests for null speedId and zero inPort cases
This commit is contained in:
sagit
2026-03-04 14:50:36 +08:00
committed by GitHub
parent 348900de01
commit a43653f252
48 changed files with 93608 additions and 15 deletions
+6 -7
View File
@@ -1426,29 +1426,28 @@ export default function ForwardPage() {
const speedLimitAutoCleared = isMissingSpeedLimit(form.speedId);
if (isEdit) {
// 更新时确保包含必要字段
const updateData = {
const updateData: ForwardMutationPayload = {
id: form.id,
userId: form.userId,
name: form.name,
tunnelId: form.tunnelId,
inPort: form.inPort,
...(isAdmin ? { inPort: form.inPort } : {}),
...(inIpTouched ? { inIp: form.inIp || "" } : {}),
remoteAddr: processedRemoteAddr,
strategy: addressCount > 1 ? form.strategy : "fifo",
speedId: normalizedSpeedId,
...(isAdmin ? { speedId: normalizedSpeedId } : {}),
};
res = await updateForward(updateData);
} else {
const createData = {
const createData: ForwardMutationPayload = {
name: form.name,
tunnelId: form.tunnelId,
inPort: form.inPort,
...(isAdmin ? { inPort: form.inPort } : {}),
inIp: form.inIp || undefined,
remoteAddr: processedRemoteAddr,
strategy: addressCount > 1 ? form.strategy : "fifo",
speedId: normalizedSpeedId,
...(isAdmin ? { speedId: normalizedSpeedId } : {}),
};
res = await createForward(createData);