internal/mcpgateway/oauth_store_test.go
Rama main · réplica de lectura del repositorio original.
package mcpgateway
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/enterpilot/gomodel/internal/encryption"
"github.com/enterpilot/gomodel/internal/encryption/encryptiontest"
)
func testOAuthRecord() oauthRecord {
return oauthRecord{
ServerURL: "https://mcp.example.com/mcp",
Resource: "https://mcp.example.com/mcp",
Issuer: "https://auth.example.com",
TokenURL: "https://auth.example.com/token",
RedirectURI: "https://gomodel.example.com/admin/mcp-oauth/callback",
ClientID: "client-1",
ClientSecret: "client-secret",
AccessToken: "access-secret",
RefreshToken: "refresh-secret",
Expiry: time.Now().Add(time.Hour).UTC().Truncate(time.Second),
SignedInAt: time.Now().UTC().Truncate(time.Second),
}
}
func TestOAuthStoreSealsRecords(t *testing.T) {
runStoreSuite(t, func(t *testing.T, raw Store) {
ctx := context.Background()
box, _ := encryptiontest.NewBox(t)
store := newOAuthStore(raw, box)
require.NotNil(t, store)
record := testOAuthRecord()
require.NoError(t, store.put(ctx, "github", record))
rows := raw.(oauthRowStore)
stored, err := rows.getOAuth(ctx, "github")
require.NoError(t, err)
assert.True(t, encryption.IsSealed(stored))
for _, secret := range []string{"access-secret", "refresh-secret", "client-secret"} {
assert.NotContains(t, stored, secret)
}
got, err := store.get(ctx, "github")
require.NoError(t, err)
assert.Equal(t, record.AccessToken, got.AccessToken)
assert.Equal(t, record.RefreshToken, got.RefreshToken)
assert.True(t, record.Expiry.Equal(got.Expiry))
// A sealed record moved to another server does not open.
require.NoError(t, rows.putOAuth(ctx, "gitlab", stored))
_, err = store.get(ctx, "gitlab")
require.Error(t, err)
listed, err := store.list(ctx)
require.Error(t, err, "the moved record is reported")
assert.Contains(t, listed, "github", "one bad record does not hide the others")
assert.NotContains(t, listed, "gitlab")
require.NoError(t, store.delete(ctx, "gitlab"))
require.NoError(t, store.delete(ctx, "github"))
_, err = store.get(ctx, "github")
require.ErrorIs(t, err, ErrNotFound)
})
}
func TestOAuthStoreReencryptsPlaintext(t *testing.T) {
runStoreSuite(t, func(t *testing.T, raw Store) {
ctx := context.Background()
plain := newOAuthStore(raw, nil)
require.NoError(t, plain.put(ctx, "github", testOAuthRecord()))
box, _ := encryptiontest.NewBox(t)
store := newOAuthStore(raw, box)
report, err := store.reencrypt(ctx)
require.NoError(t, err)
assert.Equal(t, 1, report.Reencrypted)
stored, err := raw.(oauthRowStore).getOAuth(ctx, "github")
require.NoError(t, err)
assert.True(t, encryption.IsSealed(stored))
got, err := store.get(ctx, "github")
require.NoError(t, err)
assert.Equal(t, "access-secret", got.AccessToken)
report, err = store.reencrypt(ctx)
require.NoError(t, err)
assert.Equal(t, 0, report.Reencrypted, "a second pass rewrites nothing")
})
}
// resealingRows re-seals the stored row right before the first swap, the way
// a concurrent data key rotation does.
type resealingRows struct {
oauthRowStore
reseal func() error
swaps int
}
func (r *resealingRows) swapOAuth(ctx context.Context, name, current, next string) (bool, error) {
r.swaps++
if r.swaps == 1 {
if err := r.reseal(); err != nil {
return false, err
}
}
return r.oauthRowStore.swapOAuth(ctx, name, current, next)
}
func TestOAuthStoreUpdateRetriesAfterReseal(t *testing.T) {
runStoreSuite(t, func(t *testing.T, raw Store) {
ctx := context.Background()
box, _ := encryptiontest.NewBox(t)
plain := newOAuthStore(raw, box)
record := testOAuthRecord()
require.NoError(t, plain.put(ctx, "github", record))
rows := &resealingRows{oauthRowStore: raw.(oauthRowStore)}
rows.reseal = func() error { return plain.put(ctx, "github", record) }
store := &oauthStore{rows: rows, box: box}
refreshed := record
refreshed.RefreshToken = "rotated"
saved, err := store.update(ctx, "github", refreshed, func(stored oauthRecord) bool {
return stored.SignedInAt.Equal(record.SignedInAt)
})
require.NoError(t, err)
assert.True(t, saved, "a re-sealed row with the same sign-in is written on retry")
assert.Equal(t, 2, rows.swaps)
got, err := store.get(ctx, "github")
require.NoError(t, err)
assert.Equal(t, "rotated", got.RefreshToken)
})
}