Files
elmprodvpn/selective-vpn-api/app/transport_handlers_policy_owner_locks_clear.go

117 lines
3.3 KiB
Go

package app
import (
"encoding/json"
"io"
"net/http"
"strings"
)
func handleTransportOwnerLocksClear(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var body TransportOwnerLocksClearRequest
if r.Body != nil {
defer r.Body.Close()
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<20)).Decode(&body); err != nil && err != io.EOF {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
}
filter, err := normalizeTransportOwnerLockClearRequest(body)
if err != nil {
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: false,
Message: err.Error(),
Code: "OWNER_LOCK_CLEAR_INVALID_REQUEST",
})
return
}
transportMu.Lock()
defer transportMu.Unlock()
locks := loadTransportOwnerLocksState()
baseRevision := locks.PolicyRevision
if body.BaseRevision > 0 && body.BaseRevision != baseRevision {
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: false,
Message: "stale owner-lock revision",
Code: "OWNER_LOCK_CLEAR_REVISION_MISMATCH",
BaseRevision: baseRevision,
})
return
}
matched, remaining := splitTransportOwnerLocksByFilter(locks.Items, filter)
if len(matched) == 0 {
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: true,
Message: "no matching owner locks",
BaseRevision: baseRevision,
MatchCount: 0,
ClearedCount: 0,
RemainingCount: len(locks.Items),
})
return
}
digest := digestTransportOwnerLocksClear(baseRevision, filter, matched)
confirmToken := strings.TrimSpace(body.ConfirmToken)
if !consumeTransportOwnerLocksClearToken(confirmToken, baseRevision, digest) {
nextToken := issueTransportOwnerLocksClearToken(baseRevision, digest)
code := "OWNER_LOCK_CLEAR_CONFIRM_REQUIRED"
msg := "confirm token required to clear owner locks"
if confirmToken != "" {
code = "OWNER_LOCK_CLEAR_CONFIRM_INVALID"
msg = "invalid or expired confirm token"
}
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: false,
Message: msg,
Code: code,
BaseRevision: baseRevision,
ConfirmRequired: true,
ConfirmToken: nextToken,
MatchCount: len(matched),
RemainingCount: len(locks.Items),
Items: matched,
})
return
}
next := locks
next.Items = append([]TransportOwnerLockRecord(nil), remaining...)
next.PolicyRevision = baseRevision
if err := saveTransportOwnerLocksState(next); err != nil {
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: false,
Message: "owner-lock save failed: " + err.Error(),
Code: "OWNER_LOCK_CLEAR_SAVE_FAILED",
BaseRevision: baseRevision,
})
return
}
events.push("transport_owner_locks_cleared", map[string]any{
"base_revision": baseRevision,
"cleared_count": len(matched),
"remaining_count": len(next.Items),
"client_id": filter.ClientID,
})
writeJSON(w, http.StatusOK, TransportOwnerLocksClearResponse{
OK: true,
Message: "owner locks cleared",
BaseRevision: baseRevision,
MatchCount: len(matched),
ClearedCount: len(matched),
RemainingCount: len(next.Items),
Items: matched,
})
}