mirror of
https://github.com/pgsty/minio.git
synced 2026-11-10 09:35:31 +03:00
07ce95dfda
Signed-off-by: Feng Ruohang <rh@vonng.com>
80 lines
2.4 KiB
Go
80 lines
2.4 KiB
Go
// Copyright (c) 2026 PGSTY
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Hold a lazy user load after it has read storage. This makes the ordering
|
|
// between an in-flight read and a completed credential edit deterministic.
|
|
type iamLoadUserBlocker struct {
|
|
IAMStorageAPI
|
|
key string
|
|
waiting chan struct{}
|
|
release chan struct{}
|
|
once sync.Once
|
|
}
|
|
|
|
func (s *iamLoadUserBlocker) loadUser(ctx context.Context, key string, userType IAMUserType, out map[string]UserIdentity) error {
|
|
err := s.IAMStorageAPI.loadUser(ctx, key, userType, out)
|
|
if err == nil && key == s.key && userType == regUser {
|
|
s.once.Do(func() { close(s.waiting) })
|
|
select {
|
|
case <-s.release:
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
func TestIAMLoadUserDoesNotRestoreRotatedCredential(t *testing.T) {
|
|
ctx, sys, _ := prepareIAMRevisionFixture(t)
|
|
user := iamAdminUser(ctx, t, sys, "load-user-race", "readwrite")
|
|
original := sys.store.IAMStorageAPI
|
|
blocker := &iamLoadUserBlocker{
|
|
IAMStorageAPI: original,
|
|
key: user.AccessKey,
|
|
waiting: make(chan struct{}),
|
|
release: make(chan struct{}),
|
|
}
|
|
sys.store.IAMStorageAPI = blocker
|
|
t.Cleanup(func() { sys.store.IAMStorageAPI = original })
|
|
|
|
loadDone := make(chan error, 1)
|
|
go func() { loadDone <- sys.store.LoadUser(ctx, user.AccessKey) }()
|
|
select {
|
|
case <-blocker.waiting:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("LoadUser did not reach the controlled read barrier")
|
|
}
|
|
|
|
const rotatedSecret = "load-user-rotated-secret"
|
|
if err := sys.SetUserSecretKey(ctx, user.AccessKey, rotatedSecret); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
current, valid, err := sys.CheckKey(ctx, user.AccessKey)
|
|
if err != nil || !valid || current.Credentials.SecretKey != rotatedSecret {
|
|
t.Fatalf("rotation did not update the serving cache: valid=%v secret=%q err=%v", valid, current.Credentials.SecretKey, err)
|
|
}
|
|
|
|
close(blocker.release)
|
|
select {
|
|
case err := <-loadDone:
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("LoadUser did not finish after the read barrier was released")
|
|
}
|
|
loaded, valid, err := sys.CheckKey(ctx, user.AccessKey)
|
|
if err != nil || !valid || loaded.Credentials.SecretKey != rotatedSecret {
|
|
t.Fatalf("an in-flight LoadUser restored the old credential: valid=%v secret=%q err=%v", valid, loaded.Credentials.SecretKey, err)
|
|
}
|
|
}
|