fix(review): TOCTOU лимита тенантов + персист NubesSub + пакетный http.Client
1. TenantStore.Create: повторная проверка глобального лимита тенантов под Lock (ранняя была под RLock — гонка могла превысить MaxTenantsGlobal). 2. CreateFromJWT: обновлённый NubesSub теперь персистится в Redis (раньше сохранялся только внутри ветки смены MaxQueues и терялся при рестарте); запись в Redis только при реальном изменении. 3. auth.PingNubesAPI: общий http.Client (keep-alive) вместо нового на каждый вызов.
This commit is contained in:
@@ -86,6 +86,12 @@ func (s *TenantStore) Create(name string, maxQueues int) (*Tenant, error) {
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
// Повторная проверка лимита ПОД Lock: ранняя проверка выше была под RLock,
|
||||
// между ней и взятием Lock другой поток мог создать тенанта (TOCTOU).
|
||||
if len(s.byID) >= MaxTenantsGlobal {
|
||||
s.mu.Unlock()
|
||||
return nil, fmt.Errorf("global tenant limit reached (%d)", MaxTenantsGlobal)
|
||||
}
|
||||
s.byID[t.ID] = t
|
||||
s.byAccessKey[t.AccessKey] = t
|
||||
if t.Email != "" {
|
||||
@@ -221,15 +227,21 @@ func (s *TenantStore) CreateFromJWT(sub, email string, maxQueues int) (*Tenant,
|
||||
// Идемпотентность: тенант с таким email уже есть — возвращаем как есть,
|
||||
// креды НЕ пересчитываем и НЕ меняем (они случайны и неизменны).
|
||||
if existing, ok := s.byEmail[email]; ok {
|
||||
if sub != "" {
|
||||
// Персистим только если что-то реально изменилось: иначе лишние записи
|
||||
// в Redis на каждый логин, а обновлённый NubesSub раньше вообще терялся
|
||||
// (сохранялся только внутри ветки смены MaxQueues).
|
||||
changed := false
|
||||
if sub != "" && existing.NubesSub != sub {
|
||||
existing.NubesSub = sub
|
||||
changed = true
|
||||
}
|
||||
// Обновляем лимит очередей на актуальное значение из кода при каждом
|
||||
// логине и персистим в Redis — иначе после рестарта пода из Redis
|
||||
// восстановится старый лимит (было: тенант навсегда оставался с лимитом,
|
||||
// заданным при первом логине).
|
||||
// логине — иначе после рестарта пода из Redis восстановится старый лимит.
|
||||
if existing.MaxQueues != maxQueues {
|
||||
existing.MaxQueues = maxQueues
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
if data, err := json.Marshal(existing); err == nil {
|
||||
persistence.SaveTenantRaw(existing.ID, data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user