feat(api): snapshot authentik runtime config
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
0171f9ad49
commit
eb18611dc1
5 changed files with 89 additions and 6 deletions
1
api.go
1
api.go
|
|
@ -59,6 +59,7 @@ type Engine struct {
|
|||
swaggerSecuritySchemes map[string]any
|
||||
swaggerExternalDocsDescription string
|
||||
swaggerExternalDocsURL string
|
||||
authentikConfig AuthentikConfig
|
||||
pprofEnabled bool
|
||||
expvarEnabled bool
|
||||
ssePath string
|
||||
|
|
|
|||
22
authentik.go
22
authentik.go
|
|
@ -34,6 +34,22 @@ type AuthentikConfig struct {
|
|||
PublicPaths []string
|
||||
}
|
||||
|
||||
// AuthentikConfig returns the configured Authentik settings for the engine.
|
||||
//
|
||||
// The result snapshots the Engine state at call time and clones slices so
|
||||
// callers can safely reuse or modify the returned value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// cfg := engine.AuthentikConfig()
|
||||
func (e *Engine) AuthentikConfig() AuthentikConfig {
|
||||
if e == nil {
|
||||
return AuthentikConfig{}
|
||||
}
|
||||
|
||||
return cloneAuthentikConfig(e.authentikConfig)
|
||||
}
|
||||
|
||||
// AuthentikUser represents an authenticated user extracted from Authentik
|
||||
// forward-auth headers or a validated JWT.
|
||||
//
|
||||
|
|
@ -217,6 +233,12 @@ func authentikMiddleware(cfg AuthentikConfig, publicPaths func() []string) gin.H
|
|||
}
|
||||
}
|
||||
|
||||
func cloneAuthentikConfig(cfg AuthentikConfig) AuthentikConfig {
|
||||
out := cfg
|
||||
out.PublicPaths = slices.Clone(cfg.PublicPaths)
|
||||
return out
|
||||
}
|
||||
|
||||
// RequireAuth is Gin middleware that rejects unauthenticated requests.
|
||||
// It checks for a user set by the Authentik middleware and returns 401
|
||||
// when none is present.
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ func TestEngine_RuntimeConfig_Good_SnapshotsCurrentSettings(t *testing.T) {
|
|||
api.WithWSPath("/socket"),
|
||||
api.WithSSE(broker),
|
||||
api.WithSSEPath("/events"),
|
||||
api.WithAuthentik(api.AuthentikConfig{
|
||||
Issuer: "https://auth.example.com",
|
||||
ClientID: "runtime-client",
|
||||
TrustedProxy: true,
|
||||
PublicPaths: []string{"/public", "/docs"},
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
|
@ -152,17 +158,67 @@ func TestEngine_RuntimeConfig_Good_SnapshotsCurrentSettings(t *testing.T) {
|
|||
if !slices.Equal(cfg.I18n.Supported, []string{"en-GB", "fr"}) {
|
||||
t.Fatalf("expected supported locales [en-GB fr], got %v", cfg.I18n.Supported)
|
||||
}
|
||||
if cfg.Authentik.Issuer != "https://auth.example.com" {
|
||||
t.Fatalf("expected Authentik issuer https://auth.example.com, got %q", cfg.Authentik.Issuer)
|
||||
}
|
||||
if cfg.Authentik.ClientID != "runtime-client" {
|
||||
t.Fatalf("expected Authentik client ID runtime-client, got %q", cfg.Authentik.ClientID)
|
||||
}
|
||||
if !cfg.Authentik.TrustedProxy {
|
||||
t.Fatal("expected Authentik trusted proxy to be enabled")
|
||||
}
|
||||
if !slices.Equal(cfg.Authentik.PublicPaths, []string{"/public", "/docs"}) {
|
||||
t.Fatalf("expected Authentik public paths [/public /docs], got %v", cfg.Authentik.PublicPaths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngine_RuntimeConfig_Good_EmptyOnNilEngine(t *testing.T) {
|
||||
var e *api.Engine
|
||||
|
||||
cfg := e.RuntimeConfig()
|
||||
if cfg.Swagger.Enabled || cfg.Transport.SwaggerEnabled || cfg.Cache.Enabled || cfg.I18n.DefaultLocale != "" {
|
||||
if cfg.Swagger.Enabled || cfg.Transport.SwaggerEnabled || cfg.Cache.Enabled || cfg.I18n.DefaultLocale != "" || cfg.Authentik.Issuer != "" {
|
||||
t.Fatalf("expected zero-value runtime config, got %+v", cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngine_AuthentikConfig_Good_SnapshotsCurrentSettings(t *testing.T) {
|
||||
e, _ := api.New(api.WithAuthentik(api.AuthentikConfig{
|
||||
Issuer: "https://auth.example.com",
|
||||
ClientID: "client",
|
||||
TrustedProxy: true,
|
||||
PublicPaths: []string{"/public", "/docs"},
|
||||
}))
|
||||
|
||||
cfg := e.AuthentikConfig()
|
||||
if cfg.Issuer != "https://auth.example.com" {
|
||||
t.Fatalf("expected issuer https://auth.example.com, got %q", cfg.Issuer)
|
||||
}
|
||||
if cfg.ClientID != "client" {
|
||||
t.Fatalf("expected client ID client, got %q", cfg.ClientID)
|
||||
}
|
||||
if !cfg.TrustedProxy {
|
||||
t.Fatal("expected trusted proxy to be enabled")
|
||||
}
|
||||
if !slices.Equal(cfg.PublicPaths, []string{"/public", "/docs"}) {
|
||||
t.Fatalf("expected public paths [/public /docs], got %v", cfg.PublicPaths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngine_AuthentikConfig_Good_ClonesPublicPaths(t *testing.T) {
|
||||
publicPaths := []string{"/public", "/docs"}
|
||||
e, _ := api.New(api.WithAuthentik(api.AuthentikConfig{
|
||||
Issuer: "https://auth.example.com",
|
||||
PublicPaths: publicPaths,
|
||||
}))
|
||||
|
||||
cfg := e.AuthentikConfig()
|
||||
publicPaths[0] = "/mutated"
|
||||
|
||||
if cfg.PublicPaths[0] != "/public" {
|
||||
t.Fatalf("expected snapshot to preserve original public paths, got %v", cfg.PublicPaths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngine_Register_Good_IgnoresNilGroups(t *testing.T) {
|
||||
e, _ := api.New()
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,9 @@ func WithWSPath(path string) Option {
|
|||
// api.New(api.WithAuthentik(api.AuthentikConfig{TrustedProxy: true}))
|
||||
func WithAuthentik(cfg AuthentikConfig) Option {
|
||||
return func(e *Engine) {
|
||||
e.middlewares = append(e.middlewares, authentikMiddleware(cfg, func() []string {
|
||||
snapshot := cloneAuthentikConfig(cfg)
|
||||
e.authentikConfig = snapshot
|
||||
e.middlewares = append(e.middlewares, authentikMiddleware(snapshot, func() []string {
|
||||
return []string{resolveSwaggerPath(e.swaggerPath)}
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ package api
|
|||
//
|
||||
// cfg := engine.RuntimeConfig()
|
||||
type RuntimeConfig struct {
|
||||
Swagger SwaggerConfig
|
||||
Transport TransportConfig
|
||||
Cache CacheConfig
|
||||
I18n I18nConfig
|
||||
Swagger SwaggerConfig
|
||||
Transport TransportConfig
|
||||
Cache CacheConfig
|
||||
I18n I18nConfig
|
||||
Authentik AuthentikConfig
|
||||
}
|
||||
|
||||
// RuntimeConfig returns a stable snapshot of the engine's current runtime
|
||||
|
|
@ -38,5 +39,6 @@ func (e *Engine) RuntimeConfig() RuntimeConfig {
|
|||
Transport: e.TransportConfig(),
|
||||
Cache: e.CacheConfig(),
|
||||
I18n: e.I18nConfig(),
|
||||
Authentik: e.AuthentikConfig(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue