From 77d542c282a7c7b2fc4af69675853b400bfa385e Mon Sep 17 00:00:00 2001 From: liangxu Date: Tue, 12 May 2026 12:32:39 +0800 Subject: [PATCH] feat(server): make tenant login guard toggleable via config Add auth.login_guard.enabled config (default on) so the tenant login brute-force / rate-limit guard can be disabled per environment. Surfaces an AUTH_LOGIN_GUARD_ENABLED env override and a startup warning when disabled. Wired through Diff() so hot reload picks up changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- server/configs/config.yaml | 4 ++++ server/internal/bootstrap/bootstrap.go | 7 ++++++- server/internal/shared/config/config.go | 17 +++++++++++++++++ server/internal/shared/config/reload.go | 3 +++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/server/configs/config.yaml b/server/configs/config.yaml index a00303c..4c7f4b4 100644 --- a/server/configs/config.yaml +++ b/server/configs/config.yaml @@ -163,6 +163,10 @@ jwt: access_ttl: 15m refresh_ttl: 720h +auth: + login_guard: + enabled: true + log: level: debug,info,warn,error format: json diff --git a/server/internal/bootstrap/bootstrap.go b/server/internal/bootstrap/bootstrap.go index f821588..7ff0ed6 100644 --- a/server/internal/bootstrap/bootstrap.go +++ b/server/internal/bootstrap/bootstrap.go @@ -113,7 +113,12 @@ func New(configPath string) (*App, error) { jwtMgr := auth.NewManager(cfg.JWT.Secret, cfg.JWT.AccessTTL, cfg.JWT.RefreshTTL) sessions := auth.NewSessionStore(rdb) - loginGuard := auth.NewLoginGuard(rdb, auth.DefaultLoginGuardConfig("tenant")) + loginGuardCfg := auth.DefaultLoginGuardConfig("tenant") + loginGuardCfg.Enabled = cfg.Auth.LoginGuard.Enabled + if !loginGuardCfg.Enabled { + logger.Warn("tenant login guard disabled by config; brute-force / rate-limit protection is off") + } + loginGuard := auth.NewLoginGuard(rdb, loginGuardCfg) llmClient := llm.NewReloadableClient(llm.New(cfg.LLM)) retrievalProvider := retrieval.NewReloadableProvider(retrieval.NewProvider(cfg.Retrieval, logger)) vectorStore := retrieval.NewReloadableVectorStore(retrieval.NewQdrantStore(cfg.Qdrant, logger)) diff --git a/server/internal/shared/config/config.go b/server/internal/shared/config/config.go index 3c2a600..9deb694 100644 --- a/server/internal/shared/config/config.go +++ b/server/internal/shared/config/config.go @@ -33,6 +33,7 @@ type Config struct { ObjectStorage ObjectStorageConfig `mapstructure:"object_storage"` Cache CacheConfig `mapstructure:"cache"` JWT JWTConfig `mapstructure:"jwt"` + Auth AuthConfig `mapstructure:"auth"` Log LogConfig `mapstructure:"log"` LLM LLMConfig `mapstructure:"llm"` Compliance ComplianceConfig `mapstructure:"compliance"` @@ -273,6 +274,14 @@ type JWTConfig struct { RefreshTTL time.Duration `mapstructure:"refresh_ttl"` } +type AuthConfig struct { + LoginGuard LoginGuardConfig `mapstructure:"login_guard"` +} + +type LoginGuardConfig struct { + Enabled bool `mapstructure:"enabled"` +} + type LogConfig struct { Level string `mapstructure:"level"` Format string `mapstructure:"format"` @@ -476,6 +485,11 @@ func applyConfigDefaults(settings map[string]any) { if _, ok := complianceSettings["enabled"]; !ok { complianceSettings["enabled"] = true } + authSettings := ensureMapSetting(settings, "auth") + loginGuardSettings := ensureMapSetting(authSettings, "login_guard") + if _, ok := loginGuardSettings["enabled"]; !ok { + loginGuardSettings["enabled"] = true + } } func ensureMapSetting(settings map[string]any, key string) map[string]any { @@ -800,6 +814,9 @@ func applyEnvOverrides(cfg *Config) { if ttl, ok := lookupDurationEnv("JWT_REFRESH_TTL"); ok { cfg.JWT.RefreshTTL = ttl } + if enabled, ok := lookupBoolEnv("AUTH_LOGIN_GUARD_ENABLED"); ok { + cfg.Auth.LoginGuard.Enabled = enabled + } if apiKey, ok := lookupNonEmptyEnv("LLM_API_KEY"); ok { cfg.LLM.APIKey = apiKey } diff --git a/server/internal/shared/config/reload.go b/server/internal/shared/config/reload.go index 0709aca..a116e8a 100644 --- a/server/internal/shared/config/reload.go +++ b/server/internal/shared/config/reload.go @@ -74,6 +74,9 @@ func Diff(previous, current *Config) []FieldChange { if previous.JWT != current.JWT { addChange("jwt", true) } + if previous.Auth != current.Auth { + addChange("auth", true) + } if previous.LLM != current.LLM { addChange("llm", true) }