security: address medium risks in jwt, redis ordering and body limit
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user