generated from coulomb/repo-seed
feat: implement T22, T18, T23 — dev stack, profile tests, server binary
- T22: docker-compose.dev.yml dev stack, Dockerfile, root Makefile - T18: Profile test suite (Scenario A) — 8 integration tests with real handlers - T23: Server binary wiring all components, config validation, /healthz - Config: ValidateConfig with startup validation 14 test packages pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
61
src/internal/config/validate.go
Normal file
61
src/internal/config/validate.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ValidateConfig validates a loaded Config and returns a list of human-readable
|
||||
// error messages. An empty slice means the config is valid.
|
||||
// Called at startup — the server must exit 1 if any errors are returned.
|
||||
func ValidateConfig(cfg *Config) []string {
|
||||
var errs []string
|
||||
|
||||
// Issuer must be a valid URL with an http(s) scheme.
|
||||
if cfg.Issuer == "" {
|
||||
errs = append(errs, "issuer: must not be empty")
|
||||
} else {
|
||||
u, err := url.Parse(cfg.Issuer)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
errs = append(errs, fmt.Sprintf("issuer: %q is not a valid URL (must include scheme and host)", cfg.Issuer))
|
||||
} else if u.Scheme != "http" && u.Scheme != "https" {
|
||||
errs = append(errs, fmt.Sprintf("issuer: scheme must be http or https, got %q", u.Scheme))
|
||||
}
|
||||
}
|
||||
|
||||
// Port must be in the valid TCP range.
|
||||
if cfg.Port < 1 || cfg.Port > 65535 {
|
||||
errs = append(errs, fmt.Sprintf("port: must be between 1 and 65535, got %d", cfg.Port))
|
||||
}
|
||||
|
||||
// At least one client must be registered.
|
||||
if len(cfg.Clients) == 0 {
|
||||
errs = append(errs, "clients: at least one client must be defined")
|
||||
}
|
||||
|
||||
// Each client must have at least one redirect URI and a non-empty clientId.
|
||||
for i, c := range cfg.Clients {
|
||||
prefix := fmt.Sprintf("clients[%d] (%s)", i, c.ClientID)
|
||||
if c.ClientID == "" {
|
||||
prefix = fmt.Sprintf("clients[%d]", i)
|
||||
errs = append(errs, prefix+": clientId must not be empty")
|
||||
}
|
||||
if len(c.RedirectURIs) == 0 {
|
||||
errs = append(errs, prefix+": redirect_uri: at least one redirectUri must be registered")
|
||||
}
|
||||
// Warn about wildcard redirect URIs (they are blocked at runtime anyway).
|
||||
for _, uri := range c.RedirectURIs {
|
||||
if strings.ContainsAny(uri, "*?") {
|
||||
errs = append(errs, prefix+fmt.Sprintf(": redirect_uri %q must not contain wildcards", uri))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private key PEM path must be provided (existence is checked at startup).
|
||||
if cfg.PrivateKeyPEM == "" {
|
||||
errs = append(errs, "privateKeyPem: path must not be empty")
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
Reference in New Issue
Block a user