mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
providers/proxy: move search path to query instead of runtime parameter (#20662)
Co-authored-by: Dominic R <dominic@sdko.org>
This commit is contained in:
@@ -187,10 +187,7 @@ func BuildConnConfig(cfg config.PostgreSQLConfig) (*pgx.ConnConfig, error) {
|
||||
if connConfig.RuntimeParams == nil {
|
||||
connConfig.RuntimeParams = make(map[string]string)
|
||||
}
|
||||
|
||||
if cfg.DefaultSchema != "" {
|
||||
connConfig.RuntimeParams["search_path"] = cfg.DefaultSchema
|
||||
}
|
||||
effectiveSearchPath := cfg.DefaultSchema
|
||||
|
||||
// Parse and apply connection options if specified
|
||||
if cfg.ConnOptions != "" {
|
||||
@@ -198,12 +195,39 @@ func BuildConnConfig(cfg config.PostgreSQLConfig) (*pgx.ConnConfig, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse connection options: %w", err)
|
||||
}
|
||||
// search_path from ConnOptions is not supported here; Django controls schema selection.
|
||||
// Always remove it so it cannot end up in startup RuntimeParams via applyConnOptions.
|
||||
delete(connOpts, "search_path")
|
||||
|
||||
if err := applyConnOptions(connConfig, connOpts); err != nil {
|
||||
return nil, fmt.Errorf("failed to apply connection options: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// search_path may already be present via pgx/libpq inherited defaults (e.g. service files).
|
||||
// Always remove it from startup RuntimeParams; apply it via AfterConnect instead.
|
||||
if inheritedSearchPath, hasInheritedSearchPath := connConfig.RuntimeParams["search_path"]; hasInheritedSearchPath {
|
||||
if effectiveSearchPath == "" {
|
||||
effectiveSearchPath = inheritedSearchPath
|
||||
}
|
||||
delete(connConfig.RuntimeParams, "search_path")
|
||||
}
|
||||
|
||||
// Set search_path after connection startup to avoid startup-parameter issues with PgBouncer.
|
||||
if effectiveSearchPath != "" {
|
||||
connConfig.AfterConnect = func(ctx context.Context, pgConn *pgconn.PgConn) error {
|
||||
result := pgConn.ExecParams(
|
||||
ctx,
|
||||
"select pg_catalog.set_config('search_path', $1, false)",
|
||||
[][]byte{[]byte(effectiveSearchPath)},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
).Read()
|
||||
return result.Err
|
||||
}
|
||||
}
|
||||
|
||||
return connConfig, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -700,7 +700,7 @@ func TestBuildConnConfig(t *testing.T) {
|
||||
DefaultSchema: "custom_schema",
|
||||
},
|
||||
validate: func(t *testing.T, cc *pgx.ConnConfig) {
|
||||
assert.Equal(t, "custom_schema", cc.RuntimeParams["search_path"])
|
||||
assert.NotNil(t, cc.AfterConnect)
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -756,7 +756,7 @@ func TestBuildConnConfig(t *testing.T) {
|
||||
assert.Equal(t, "admin", cc.User)
|
||||
assert.Equal(t, "my super secret password!@#", cc.Password)
|
||||
assert.Equal(t, "production", cc.Database)
|
||||
assert.Equal(t, "app_schema", cc.RuntimeParams["search_path"])
|
||||
assert.NotNil(t, cc.AfterConnect)
|
||||
assert.Equal(t, "authentik", cc.RuntimeParams["application_name"])
|
||||
},
|
||||
},
|
||||
@@ -863,7 +863,7 @@ func TestBuildConnConfig_WithSSLCertificates(t *testing.T) {
|
||||
assert.Equal(t, "db.example.com", cc.TLSConfig.ServerName)
|
||||
assert.NotNil(t, cc.TLSConfig.RootCAs)
|
||||
assert.Len(t, cc.TLSConfig.Certificates, 1)
|
||||
assert.Equal(t, "app_schema", cc.RuntimeParams["search_path"])
|
||||
assert.NotNil(t, cc.AfterConnect)
|
||||
assert.Equal(t, "authentik", cc.RuntimeParams["application_name"])
|
||||
},
|
||||
},
|
||||
@@ -1357,6 +1357,83 @@ func TestBuildConnConfig_WithBase64EncodedConnOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies DefaultSchema is applied via AfterConnect and never via startup RuntimeParams.
|
||||
func TestBuildConnConfig_SearchPath_DefaultSchema(t *testing.T) {
|
||||
cfg := config.PostgreSQLConfig{
|
||||
Host: "localhost",
|
||||
Port: "5432",
|
||||
User: "authentik",
|
||||
Name: "authentik",
|
||||
DefaultSchema: "default_schema",
|
||||
}
|
||||
|
||||
connConfig, err := BuildConnConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, connConfig.AfterConnect)
|
||||
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
|
||||
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
|
||||
}
|
||||
|
||||
// Verifies ConnOptions search_path is ignored and excluded from startup RuntimeParams.
|
||||
func TestBuildConnConfig_SearchPath_ConnOptions(t *testing.T) {
|
||||
cfg := config.PostgreSQLConfig{
|
||||
Host: "localhost",
|
||||
Port: "5432",
|
||||
User: "authentik",
|
||||
Name: "authentik",
|
||||
ConnOptions: base64.StdEncoding.EncodeToString([]byte(`{"search_path":"connopt_schema"}`)),
|
||||
}
|
||||
|
||||
connConfig, err := BuildConnConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, connConfig.AfterConnect)
|
||||
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
|
||||
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
|
||||
}
|
||||
|
||||
// Verifies ConnOptions search_path does not override DefaultSchema and other conn options still apply.
|
||||
func TestBuildConnConfig_SearchPath_ConnOptionsIgnoredWhenDefaultSchemaSet(t *testing.T) {
|
||||
cfg := config.PostgreSQLConfig{
|
||||
Host: "localhost",
|
||||
Port: "5432",
|
||||
User: "authentik",
|
||||
Name: "authentik",
|
||||
DefaultSchema: "default_schema",
|
||||
ConnOptions: base64.StdEncoding.EncodeToString([]byte(`{"search_path":"override_schema","application_name":"authentik-proxy"}`)),
|
||||
}
|
||||
|
||||
connConfig, err := BuildConnConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, connConfig.AfterConnect)
|
||||
assert.Equal(t, "authentik-proxy", connConfig.RuntimeParams["application_name"])
|
||||
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
|
||||
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
|
||||
}
|
||||
|
||||
// Verifies inherited search_path from pgx/libpq defaults is removed from startup RuntimeParams.
|
||||
func TestBuildConnConfig_SearchPath_InheritedServiceSetting(t *testing.T) {
|
||||
serviceFile := filepath.Join(t.TempDir(), "pg_service.conf")
|
||||
err := os.WriteFile(serviceFile, []byte("[authentik-test]\nsearch_path=service_schema\n"), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Setenv("PGSERVICE", "authentik-test")
|
||||
t.Setenv("PGSERVICEFILE", serviceFile)
|
||||
|
||||
cfg := config.PostgreSQLConfig{
|
||||
Host: "localhost",
|
||||
Port: "5432",
|
||||
User: "authentik",
|
||||
Name: "authentik",
|
||||
}
|
||||
|
||||
connConfig, err := BuildConnConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, connConfig.AfterConnect)
|
||||
|
||||
_, hasSearchPath := connConfig.RuntimeParams["search_path"]
|
||||
assert.False(t, hasSearchPath, "search_path should not appear in RuntimeParams")
|
||||
}
|
||||
|
||||
// TestBuildConnConfig_TargetSessionAttrs demonstrates how target_session_attrs
|
||||
// should be properly handled using pgx's ValidateConnect callback
|
||||
func TestBuildConnConfig_TargetSessionAttrs(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user