security: address medium risks in jwt, redis ordering and body limit

This commit is contained in:
Naeel
2026-04-10 19:44:34 +03:00
parent a5e9bfb15c
commit 3b4fe0bb3a
3 changed files with 61 additions and 3 deletions
+49
View File
@@ -12,6 +12,7 @@ import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/redis/go-redis/v9"
@@ -24,6 +25,12 @@ import (
// nil означает режим "только память" — все функции тихо no-op.
var Client *redis.Client
var (
seqMu sync.Mutex
queueWriteSeq = map[string]uint64{}
tenantWriteSeq = map[string]uint64{}
)
const (
// redisHashTenants — HASH: tenantID → JSON тенанта
redisHashTenants = "ssq:tenants"
@@ -85,7 +92,11 @@ func SaveQueue(key string, queue *models.Queue) {
log.Warnf("persistence: queue %q too large for Redis (%d bytes), skipping", key, len(data))
return
}
writeSeq := nextQueueWriteSeq(key)
asyncWrite(func() {
if !isLatestQueueWriteSeq(key, writeSeq) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := Client.HSet(ctx, redisHashQueues, key, string(data)).Err(); err != nil {
@@ -99,7 +110,11 @@ func DeleteQueue(key string) {
if Client == nil {
return
}
writeSeq := nextQueueWriteSeq(key)
asyncWrite(func() {
if !isLatestQueueWriteSeq(key, writeSeq) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := Client.HDel(ctx, redisHashQueues, key).Err(); err != nil {
@@ -153,7 +168,11 @@ func SaveTenantRaw(id string, jsonData []byte) {
// Копируем bytes — вызывающий может переиспользовать буфер
dataCopy := make([]byte, len(jsonData))
copy(dataCopy, jsonData)
writeSeq := nextTenantWriteSeq(id)
asyncWrite(func() {
if !isLatestTenantWriteSeq(id, writeSeq) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := Client.HSet(ctx, redisHashTenants, id, string(dataCopy)).Err(); err != nil {
@@ -167,7 +186,11 @@ func DeleteTenant(id string) {
if Client == nil {
return
}
writeSeq := nextTenantWriteSeq(id)
asyncWrite(func() {
if !isLatestTenantWriteSeq(id, writeSeq) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := Client.HDel(ctx, redisHashTenants, id).Err(); err != nil {
@@ -176,6 +199,32 @@ func DeleteTenant(id string) {
})
}
func nextQueueWriteSeq(key string) uint64 {
seqMu.Lock()
defer seqMu.Unlock()
queueWriteSeq[key]++
return queueWriteSeq[key]
}
func isLatestQueueWriteSeq(key string, seq uint64) bool {
seqMu.Lock()
defer seqMu.Unlock()
return queueWriteSeq[key] == seq
}
func nextTenantWriteSeq(id string) uint64 {
seqMu.Lock()
defer seqMu.Unlock()
tenantWriteSeq[id]++
return tenantWriteSeq[id]
}
func isLatestTenantWriteSeq(id string, seq uint64) bool {
seqMu.Lock()
defer seqMu.Unlock()
return tenantWriteSeq[id] == seq
}
// LoadAllTenantsRaw — загружает всех тенантов из Redis при старте.
// Возвращает map[tenantID]rawJSON — десериализацию делает tenant_store.go.
func LoadAllTenantsRaw() (map[string][]byte, error) {