Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/server/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/server/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions docs/server/swagger.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

202 changes: 93 additions & 109 deletions go.mod

Large diffs are not rendered by default.

104 changes: 50 additions & 54 deletions go.sum

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions pkg/authserver/runner/embeddedauthserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"sync"
"time"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
"github.com/stacklok/toolhive/pkg/auth/dcr"
"github.com/stacklok/toolhive/pkg/authserver"
servercrypto "github.com/stacklok/toolhive/pkg/authserver/server/crypto"
Expand Down Expand Up @@ -972,35 +972,35 @@ func createStorage(ctx context.Context, cfg *storage.RunConfig) (storage.Storage
}

// convertRedisRunConfig converts a serializable RedisRunConfig to a runtime
// tcredis.Config. It resolves ACL credentials from environment variables and
// redisconn.Config. It resolves ACL credentials from environment variables and
// parses duration strings. Connection-mode topology and defaulting are handled
// by the shared toolhive-core redis package when the client is constructed.
// by the shared toolhive-core redisconn package when the client is constructed.
//
// An ACL config is not required: a nil rc.ACLUserConfig produces a no-auth
// connection (for a Redis/Valkey instance running without authentication) and
// emits one startup WARN naming the store. A populated ACL config whose
// password resolves to empty is still rejected — see convertRedisACLConfig.
func convertRedisRunConfig(rc *storage.RedisRunConfig) (tcredis.Config, error) {
func convertRedisRunConfig(rc *storage.RedisRunConfig) (redisconn.Config, error) {
if rc == nil {
return tcredis.Config{}, fmt.Errorf("redis config is required when storage type is redis")
return redisconn.Config{}, fmt.Errorf("redis config is required when storage type is redis")
}

// AuthType declares authenticated intent. If it selects ACL-user auth, a
// nil ACLUserConfig is a misconfiguration, not a no-auth request: fail
// loudly rather than silently downgrading to an unauthenticated connection.
// An empty AuthType with a nil ACLUserConfig remains a valid no-auth config.
if rc.AuthType == storage.AuthTypeACLUser && rc.ACLUserConfig == nil {
return tcredis.Config{}, fmt.Errorf(
return redisconn.Config{}, fmt.Errorf(
"auth_type %q requires acl_user_config; omit auth_type for a no-auth connection", rc.AuthType)
}

cfg := tcredis.Config{
cfg := redisconn.Config{
Addr: rc.Addr,
ClusterMode: rc.ClusterMode,
}

if rc.SentinelConfig != nil {
cfg.SentinelConfig = &tcredis.SentinelConfig{
cfg.SentinelConfig = &redisconn.SentinelConfig{
MasterName: rc.SentinelConfig.MasterName,
SentinelAddrs: rc.SentinelConfig.SentinelAddrs,
}
Expand All @@ -1009,26 +1009,26 @@ func convertRedisRunConfig(rc *storage.RedisRunConfig) (tcredis.Config, error) {

acl, err := convertRedisACLConfig(rc.ACLUserConfig)
if err != nil {
return tcredis.Config{}, fmt.Errorf("failed to convert ACL config: %w", err)
return redisconn.Config{}, fmt.Errorf("failed to convert ACL config: %w", err)
}
cfg.Username = acl.username
cfg.Password = acl.password

if err := applyRedisTimeouts(rc, &cfg); err != nil {
return tcredis.Config{}, fmt.Errorf("failed to apply redis timeouts: %w", err)
return redisconn.Config{}, fmt.Errorf("failed to apply redis timeouts: %w", err)
}

tlsCfg, err := convertRedisTLSRunConfig(rc.TLS)
if err != nil {
return tcredis.Config{}, fmt.Errorf("master TLS config: %w", err)
return redisconn.Config{}, fmt.Errorf("master TLS config: %w", err)
}
cfg.TLS = tlsCfg

// SentinelTLS only applies in Sentinel mode
if rc.SentinelConfig != nil {
sentinelTLSCfg, err := convertRedisTLSRunConfig(rc.SentinelTLS)
if err != nil {
return tcredis.Config{}, fmt.Errorf("sentinel TLS config: %w", err)
return redisconn.Config{}, fmt.Errorf("sentinel TLS config: %w", err)
}
cfg.SentinelTLS = sentinelTLSCfg
}
Expand Down Expand Up @@ -1121,7 +1121,7 @@ func redisStoreName(rc *storage.RedisRunConfig) string {
}

// applyRedisTimeouts parses and applies optional timeout duration strings to cfg.
func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *tcredis.Config) error {
func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *redisconn.Config) error {
if rc.DialTimeout != "" {
d, err := time.ParseDuration(rc.DialTimeout)
if err != nil {
Expand All @@ -1147,15 +1147,15 @@ func applyRedisTimeouts(rc *storage.RedisRunConfig, cfg *tcredis.Config) error {
}

// convertRedisTLSRunConfig converts a RedisTLSRunConfig to a runtime
// tcredis.TLSConfig. Returns an error if a CA cert file is configured but
// redisconn.TLSConfig. Returns an error if a CA cert file is configured but
// cannot be read — this is treated as a hard error because silently falling
// back to system CAs could mask a misconfiguration and cause confusing TLS
// failures downstream.
func convertRedisTLSRunConfig(rc *storage.RedisTLSRunConfig) (*tcredis.TLSConfig, error) {
func convertRedisTLSRunConfig(rc *storage.RedisTLSRunConfig) (*redisconn.TLSConfig, error) {
if rc == nil {
return nil, nil
}
cfg := &tcredis.TLSConfig{
cfg := &redisconn.TLSConfig{
InsecureSkipVerify: rc.InsecureSkipVerify,
}
if rc.CACertFile != "" {
Expand Down
8 changes: 4 additions & 4 deletions pkg/authserver/storage/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/ory/fosite"
"github.com/redis/go-redis/v9"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
"github.com/stacklok/toolhive/pkg/authserver/server"
"github.com/stacklok/toolhive/pkg/authserver/server/registration"
"github.com/stacklok/toolhive/pkg/authserver/server/session"
Expand Down Expand Up @@ -127,15 +127,15 @@ type storedSession struct {
//
// Connection-mode validation, timeout defaults, client construction (standalone,
// cluster, or sentinel), TLS plumbing, and connectivity verification are
// delegated to the shared toolhive-core redis package. cfg.Password may be
// delegated to the shared toolhive-core redisconn package. cfg.Password may be
// empty when the Redis server does not require authentication (the auth server
// does not mandate ACL auth); the keyPrefix is storage-specific and required.
func NewRedisStorage(ctx context.Context, cfg tcredis.Config, keyPrefix string) (*RedisStorage, error) {
func NewRedisStorage(ctx context.Context, cfg redisconn.Config, keyPrefix string) (*RedisStorage, error) {
if keyPrefix == "" {
return nil, errors.New("invalid redis configuration: key prefix is required")
}

client, err := tcredis.NewClient(ctx, &cfg)
client, err := redisconn.NewClient(ctx, &cfg)
if err != nil {
return nil, err
}
Expand Down
24 changes: 12 additions & 12 deletions pkg/authserver/storage/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
"github.com/stacklok/toolhive/pkg/authserver/server/registration"
"github.com/stacklok/toolhive/pkg/authserver/server/session"
"github.com/stacklok/toolhive/pkg/oauthproto"
Expand Down Expand Up @@ -124,18 +124,18 @@ func newDCRClient(t *testing.T, id, method, secret string) fosite.Client {
// TestNewRedisStorage_Validation covers the auth-server-specific invariants
// enforced by NewRedisStorage. Connection-mode topology (Addr XOR Sentinel,
// cluster requires Addr, sentinel master name and addresses) and credentials
// are validated by the shared toolhive-core redis package and exercised in its
// are validated by the shared toolhive-core redisconn package and exercised in its
// own tests; NewRedisStorage does not mandate a password.
func TestNewRedisStorage_Validation(t *testing.T) {
t.Parallel()

validCfg := func() tcredis.Config {
return tcredis.Config{Addr: "localhost:6379", Username: "user", Password: "pass"}
validCfg := func() redisconn.Config {
return redisconn.Config{Addr: "localhost:6379", Username: "user", Password: "pass"}
}

tests := []struct {
name string
cfg tcredis.Config
cfg redisconn.Config
keyPrefix string
wantErr string
}{
Expand Down Expand Up @@ -163,12 +163,12 @@ func TestNewRedisStorage_ConnectionFailure(t *testing.T) {

tests := []struct {
name string
cfg tcredis.Config
cfg redisconn.Config
}{
{
name: "sentinel mode",
cfg: tcredis.Config{
SentinelConfig: &tcredis.SentinelConfig{
cfg: redisconn.Config{
SentinelConfig: &redisconn.SentinelConfig{
MasterName: "mymaster",
SentinelAddrs: []string{"localhost:99999"}, // Invalid port
},
Expand All @@ -179,7 +179,7 @@ func TestNewRedisStorage_ConnectionFailure(t *testing.T) {
},
{
name: "standalone mode",
cfg: tcredis.Config{
cfg: redisconn.Config{
Addr: "localhost:19999",
Username: "user",
Password: "pass",
Expand All @@ -188,7 +188,7 @@ func TestNewRedisStorage_ConnectionFailure(t *testing.T) {
},
{
name: "cluster mode",
cfg: tcredis.Config{
cfg: redisconn.Config{
Addr: "localhost:19998",
ClusterMode: true,
Username: "user",
Expand Down Expand Up @@ -219,7 +219,7 @@ func TestNewRedisStorage_Standalone_WithMiniredis(t *testing.T) {
// supplied, so we use RequireUserAuth to match the configured username/password.
mr.RequireUserAuth("testuser", "testpass")

cfg := tcredis.Config{
cfg := redisconn.Config{
Addr: mr.Addr(),
Username: "testuser",
Password: "testpass",
Expand All @@ -241,7 +241,7 @@ func TestNewRedisStorage_Standalone_Passwordless(t *testing.T) {

mr := miniredis.RunT(t) // no RequireUserAuth → auth disabled

cfg := tcredis.Config{Addr: mr.Addr()}
cfg := redisconn.Config{Addr: mr.Addr()}

ctx := context.Background()
s, err := NewRedisStorage(ctx, cfg, "test:")
Expand Down
4 changes: 2 additions & 2 deletions pkg/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

"golang.org/x/oauth2"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
"github.com/stacklok/toolhive/pkg/auth"
"github.com/stacklok/toolhive/pkg/auth/remote"
authsecrets "github.com/stacklok/toolhive/pkg/auth/secrets"
Expand Down Expand Up @@ -514,7 +514,7 @@ func (r *Runner) Run(ctx context.Context) error {
if keyPrefix == "" {
keyPrefix = "thv:proxy:session:"
}
storage, err := session.NewRedisStorage(ctx, tcredis.Config{
storage, err := session.NewRedisStorage(ctx, redisconn.Config{
Addr: redisCfg.Address,
Password: os.Getenv(session.RedisPasswordEnvVar),
DB: int(redisCfg.DB),
Expand Down
4 changes: 2 additions & 2 deletions pkg/transport/session/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/google/uuid"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
)

const (
Expand Down Expand Up @@ -130,7 +130,7 @@ func NewManagerWithRedis(
ctx context.Context,
ttl time.Duration,
factory Factory,
cfg tcredis.Config,
cfg redisconn.Config,
keyPrefix string,
) (*Manager, error) {
storage, err := NewRedisStorage(ctx, cfg, keyPrefix, ttl)
Expand Down
10 changes: 5 additions & 5 deletions pkg/transport/session/manager_redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
)

func proxyFactory(id string) Session { return NewProxySession(id) }
Expand All @@ -29,7 +29,7 @@ func TestNewManagerWithRedis(t *testing.T) {
context.Background(),
time.Hour,
proxyFactory,
tcredis.Config{Addr: mr.Addr()},
redisconn.Config{Addr: mr.Addr()},
"test:mgr:",
)
require.NoError(t, err)
Expand All @@ -47,7 +47,7 @@ func TestNewManagerWithRedis(t *testing.T) {
context.Background(),
time.Hour,
proxyFactory,
tcredis.Config{Addr: "localhost:6379"},
redisconn.Config{Addr: "localhost:6379"},
"",
)
require.Error(t, err)
Expand All @@ -63,7 +63,7 @@ func TestNewManagerWithRedis(t *testing.T) {
context.Background(),
time.Hour,
proxyFactory,
tcredis.Config{Addr: mr.Addr()},
redisconn.Config{Addr: mr.Addr()},
"test:mgr:",
)
require.NoError(t, err)
Expand All @@ -86,7 +86,7 @@ func TestNewManagerWithRedis(t *testing.T) {
context.Background(),
time.Hour,
proxyFactory,
tcredis.Config{Addr: mr.Addr()},
redisconn.Config{Addr: mr.Addr()},
"test:mgr:",
)
require.NoError(t, err)
Expand Down
6 changes: 3 additions & 3 deletions pkg/transport/session/session_data_storage_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/redis/go-redis/v9"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
)

// RedisSessionDataStorage implements DataStorage backed by Redis/Valkey.
Expand Down Expand Up @@ -40,14 +40,14 @@ type RedisSessionDataStorage struct {
// delegated to the shared toolhive-core redis package.
func NewRedisSessionDataStorage(
ctx context.Context,
cfg tcredis.Config,
cfg redisconn.Config,
keyPrefix string,
ttl time.Duration,
) (*RedisSessionDataStorage, error) {
if err := validateSessionInvariants(keyPrefix, ttl); err != nil {
return nil, err
}
client, err := tcredis.NewClient(ctx, &cfg)
client, err := redisconn.NewClient(ctx, &cfg)
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/transport/session/storage_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

"github.com/redis/go-redis/v9"

tcredis "github.com/stacklok/toolhive-core/redis"
"github.com/stacklok/toolhive-core/redisconn"
)

// RedisStorage implements the Storage interface backed by Redis.
Expand All @@ -30,12 +30,12 @@ type RedisStorage struct {
//
// Connection-mode validation, timeout defaults, client construction (standalone,
// cluster, or sentinel), TLS plumbing, and connectivity verification are
// delegated to the shared toolhive-core redis package.
func NewRedisStorage(ctx context.Context, cfg tcredis.Config, keyPrefix string, ttl time.Duration) (*RedisStorage, error) {
// delegated to the shared toolhive-core redisconn package.
func NewRedisStorage(ctx context.Context, cfg redisconn.Config, keyPrefix string, ttl time.Duration) (*RedisStorage, error) {
if err := validateSessionInvariants(keyPrefix, ttl); err != nil {
return nil, err
}
client, err := tcredis.NewClient(ctx, &cfg)
client, err := redisconn.NewClient(ctx, &cfg)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading