Skip to content

Commit 2c24f26

Browse files
committed
Add whitelist registry CLI and integration tests
- Add --registry, --whitelist, --whitelist-watch CLI flags - Add --config flag for YAML configuration file support - Fix healthz/readyz 404 by adding RegisterHealthEndpoints to RegisterAPIRoutes - Add ETSI registry integration tests (5 tests) - Add OIDF registry integration tests (8 tests) - Add whitelist registry integration tests (3 tests) - Update documentation and examples
1 parent 871a17c commit 2c24f26

File tree

10 files changed

+1128
-18
lines changed

10 files changed

+1128
-18
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ gt --etsi-cert-bundle /path/to/trusted-certs.pem
6161
# With TSL XML files directly
6262
gt --etsi-tsl-files eu-lotl.xml,se-tsl.xml
6363

64+
# With whitelist registry (for simple deployments)
65+
gt --registry whitelist --whitelist /etc/go-trust/whitelist.yaml
66+
67+
# With always-trusted registry (development/testing only!)
68+
gt --registry always-trusted
69+
70+
# With configuration file
71+
gt --config /etc/go-trust/config.yaml
72+
6473
# With external URL for discovery (behind reverse proxy)
6574
gt --external-url https://pdp.example.com --etsi-cert-bundle certs.pem
6675

@@ -83,6 +92,9 @@ gt \
8392
| `--external-url` | External URL for discovery | auto-detect |
8493
| `--etsi-cert-bundle` | PEM file with trusted CA certs | - |
8594
| `--etsi-tsl-files` | Comma-separated TSL XML files | - |
95+
| `--registry` | Registry type: whitelist, always-trusted, never-trusted | - |
96+
| `--whitelist` | Path to whitelist YAML/JSON config file | - |
97+
| `--whitelist-watch` | Watch whitelist file for changes | `true` |
8698
| `--log-level` | Log level: debug, info, warn, error | `info` |
8799
| `--log-format` | Log format: text, json | `text` |
88100
| `--config` | Configuration file (YAML) | - |

cmd/gt/main.go

Lines changed: 181 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import (
1010
"github.com/sirosfoundation/g119612/pkg/logging"
1111
_ "github.com/sirosfoundation/go-trust/docs/swagger" // Import generated docs
1212
"github.com/sirosfoundation/go-trust/pkg/api"
13+
"github.com/sirosfoundation/go-trust/pkg/config"
1314
"github.com/sirosfoundation/go-trust/pkg/registry"
1415
"github.com/sirosfoundation/go-trust/pkg/registry/etsi"
16+
"github.com/sirosfoundation/go-trust/pkg/registry/static"
1517
swaggerFiles "github.com/swaggo/files"
1618
ginSwagger "github.com/swaggo/gin-swagger"
1719
)
@@ -67,13 +69,18 @@ func usage() {
6769
fmt.Fprintln(os.Stderr, "\nETSI TSL Registry Options:")
6870
fmt.Fprintln(os.Stderr, " --etsi-cert-bundle Path to PEM file with trusted CA certificates")
6971
fmt.Fprintln(os.Stderr, " --etsi-tsl-files Comma-separated list of local TSL XML files")
72+
fmt.Fprintln(os.Stderr, "\nWhitelist Registry Options:")
73+
fmt.Fprintln(os.Stderr, " --registry Registry type: whitelist, always-trusted, never-trusted")
74+
fmt.Fprintln(os.Stderr, " --whitelist Path to whitelist YAML/JSON config file")
75+
fmt.Fprintln(os.Stderr, " --whitelist-watch Watch whitelist file for changes (default: true)")
7076
fmt.Fprintln(os.Stderr, "\nLogging Options:")
7177
fmt.Fprintln(os.Stderr, " --log-level Logging level: debug, info, warn, error (default: info)")
7278
fmt.Fprintln(os.Stderr, " --log-format Logging format: text or json (default: text)")
7379
fmt.Fprintln(os.Stderr, "\nNotes:")
7480
fmt.Fprintln(os.Stderr, " - For TSL processing (load, transform, sign, publish), use tsl-tool from g119612")
7581
fmt.Fprintln(os.Stderr, " - This server consumes pre-processed TSL data (PEM bundles or XML files)")
7682
fmt.Fprintln(os.Stderr, " - Run tsl-tool via cron to update TSL data periodically")
83+
fmt.Fprintln(os.Stderr, " - Use --registry=whitelist --whitelist=/path/to/whitelist.yaml for simple deployments")
7784
fmt.Fprintln(os.Stderr, "")
7885
}
7986

@@ -89,6 +96,11 @@ func main() {
8996
etsiCertBundle := flag.String("etsi-cert-bundle", "", "Path to PEM file with trusted CA certificates")
9097
etsiTSLFiles := flag.String("etsi-tsl-files", "", "Comma-separated list of local TSL XML files")
9198

99+
// Whitelist/static registry options
100+
registryType := flag.String("registry", "", "Registry type: whitelist, always-trusted, never-trusted")
101+
whitelistFile := flag.String("whitelist", "", "Path to whitelist YAML/JSON config file")
102+
whitelistWatch := flag.Bool("whitelist-watch", true, "Watch whitelist file for changes")
103+
92104
// Logging options
93105
logLevel := flag.String("log-level", "info", "Logging level: debug, info, warn, error")
94106
logFormat := flag.String("log-format", "text", "Logging format: text or json")
@@ -133,10 +145,35 @@ func main() {
133145
logging.F("host", *host),
134146
logging.F("port", *port))
135147

136-
// TODO: Load configuration file if provided
148+
// Load configuration file if provided
149+
var cfg *config.Config
137150
if *configFile != "" {
138-
logger.Warn("Configuration file support not yet implemented",
151+
var err error
152+
cfg, err = config.LoadConfig(*configFile)
153+
if err != nil {
154+
logger.Fatal("Failed to load configuration file",
155+
logging.F("file", *configFile),
156+
logging.F("error", err.Error()))
157+
}
158+
logger.Info("Loaded configuration from file",
139159
logging.F("file", *configFile))
160+
161+
// Use config file values if CLI flags weren't explicitly set
162+
if *host == "127.0.0.1" && cfg.Server.Host != "" {
163+
*host = cfg.Server.Host
164+
}
165+
if *port == "6001" && cfg.Server.Port != "" {
166+
*port = cfg.Server.Port
167+
}
168+
if *externalURL == "" && cfg.Server.ExternalURL != "" {
169+
*externalURL = cfg.Server.ExternalURL
170+
}
171+
if *logLevel == "info" && cfg.Logging.Level != "" {
172+
*logLevel = cfg.Logging.Level
173+
}
174+
if *logFormat == "text" && cfg.Logging.Format != "" {
175+
*logFormat = cfg.Logging.Format
176+
}
140177
}
141178

142179
// Create server context
@@ -145,7 +182,12 @@ func main() {
145182
// Initialize RegistryManager
146183
registryMgr := registry.NewRegistryManager(registry.FirstMatch, 30*time.Second)
147184

148-
// Configure ETSI TSL registry if cert bundle or TSL files provided
185+
// Configure registries from config file
186+
if cfg != nil {
187+
configureRegistriesFromConfig(cfg, registryMgr, logger)
188+
}
189+
190+
// CLI flags override config file - Configure ETSI TSL registry if cert bundle or TSL files provided
149191
if *etsiCertBundle != "" || *etsiTSLFiles != "" {
150192
logger.Info("Configuring ETSI TSL registry")
151193

@@ -176,9 +218,40 @@ func main() {
176218

177219
registryMgr.Register(tslRegistry)
178220
logger.Info("ETSI TSL registry registered")
179-
} else {
180-
logger.Warn("No ETSI TSL configuration provided (use --etsi-cert-bundle or --etsi-tsl-files)")
181-
logger.Warn("Server will start but ETSI trust evaluation will not be available")
221+
}
222+
223+
// Configure whitelist/static registry if requested via CLI
224+
switch *registryType {
225+
case "whitelist":
226+
if *whitelistFile != "" {
227+
logger.Info("Configuring whitelist registry from file",
228+
logging.F("path", *whitelistFile),
229+
logging.F("watch", *whitelistWatch))
230+
whitelistReg, err := static.NewWhitelistRegistryFromFile(*whitelistFile, *whitelistWatch,
231+
static.WithWhitelistName("whitelist"),
232+
static.WithWhitelistDescription("URL whitelist from "+*whitelistFile))
233+
if err != nil {
234+
logger.Fatal("Failed to create whitelist registry",
235+
logging.F("error", err.Error()))
236+
}
237+
registryMgr.Register(whitelistReg)
238+
logger.Info("Whitelist registry registered")
239+
} else {
240+
logger.Fatal("--whitelist flag required when using --registry=whitelist")
241+
}
242+
case "always-trusted":
243+
logger.Warn("Using always-trusted registry - ALL trust requests will be approved")
244+
logger.Warn("This is only suitable for development/testing!")
245+
registryMgr.Register(static.NewAlwaysTrustedRegistry("always-trusted"))
246+
case "never-trusted":
247+
logger.Info("Using never-trusted registry - ALL trust requests will be denied")
248+
registryMgr.Register(static.NewNeverTrustedRegistry("never-trusted"))
249+
case "":
250+
// No static registry configured
251+
default:
252+
logger.Fatal("Unknown registry type",
253+
logging.F("type", *registryType),
254+
logging.F("valid", "whitelist, always-trusted, never-trusted"))
182255
}
183256

184257
// TODO: Add other registries (OpenID Federation, DID Web) based on config
@@ -219,6 +292,108 @@ func main() {
219292
}
220293
}
221294

295+
// configureRegistriesFromConfig configures registries from the loaded config file.
296+
func configureRegistriesFromConfig(cfg *config.Config, registryMgr *registry.RegistryManager, logger logging.Logger) {
297+
// Configure ETSI TSL registry from config
298+
if cfg.Registries.ETSI != nil && cfg.Registries.ETSI.Enabled {
299+
logger.Info("Configuring ETSI TSL registry from config file")
300+
etsiCfg := cfg.Registries.ETSI
301+
302+
tslConfig := etsi.TSLConfig{
303+
Name: etsiCfg.Name,
304+
Description: etsiCfg.Description,
305+
CertBundle: etsiCfg.CertBundle,
306+
TSLFiles: etsiCfg.TSLFiles,
307+
}
308+
309+
if tslConfig.Name == "" {
310+
tslConfig.Name = "ETSI-TSL"
311+
}
312+
if tslConfig.Description == "" {
313+
tslConfig.Description = "ETSI TS 119612 Trust Status List Registry"
314+
}
315+
316+
tslRegistry, err := etsi.NewTSLRegistry(tslConfig)
317+
if err != nil {
318+
logger.Fatal("Failed to create ETSI TSL registry from config",
319+
logging.F("error", err.Error()))
320+
}
321+
322+
registryMgr.Register(tslRegistry)
323+
logger.Info("ETSI TSL registry registered from config")
324+
}
325+
326+
// Configure whitelist registry from config
327+
if cfg.Registries.Whitelist != nil && cfg.Registries.Whitelist.Enabled {
328+
logger.Info("Configuring whitelist registry from config file")
329+
wlCfg := cfg.Registries.Whitelist
330+
331+
name := wlCfg.Name
332+
if name == "" {
333+
name = "whitelist"
334+
}
335+
desc := wlCfg.Description
336+
if desc == "" {
337+
desc = "Static URL Whitelist"
338+
}
339+
340+
var whitelistReg *static.WhitelistRegistry
341+
var err error
342+
343+
if wlCfg.ConfigFile != "" {
344+
// Load from external config file
345+
whitelistReg, err = static.NewWhitelistRegistryFromFile(
346+
wlCfg.ConfigFile,
347+
wlCfg.WatchFile,
348+
static.WithWhitelistName(name),
349+
static.WithWhitelistDescription(desc),
350+
)
351+
if err != nil {
352+
logger.Fatal("Failed to create whitelist registry from config file",
353+
logging.F("config_file", wlCfg.ConfigFile),
354+
logging.F("error", err.Error()))
355+
}
356+
} else {
357+
// Use inline configuration
358+
whitelistReg = static.NewWhitelistRegistry(
359+
static.WithWhitelistName(name),
360+
static.WithWhitelistDescription(desc),
361+
static.WithWhitelistConfig(static.WhitelistConfig{
362+
Issuers: wlCfg.Issuers,
363+
Verifiers: wlCfg.Verifiers,
364+
TrustedSubjects: wlCfg.TrustedSubjects,
365+
}),
366+
)
367+
}
368+
369+
registryMgr.Register(whitelistReg)
370+
logger.Info("Whitelist registry registered from config",
371+
logging.F("issuers", len(wlCfg.Issuers)),
372+
logging.F("verifiers", len(wlCfg.Verifiers)))
373+
}
374+
375+
// Configure always-trusted registry from config
376+
if cfg.Registries.AlwaysTrusted != nil && cfg.Registries.AlwaysTrusted.Enabled {
377+
logger.Warn("Configuring always-trusted registry from config")
378+
logger.Warn("ALL trust requests will be approved - only suitable for development/testing!")
379+
name := cfg.Registries.AlwaysTrusted.Name
380+
if name == "" {
381+
name = "always-trusted"
382+
}
383+
registryMgr.Register(static.NewAlwaysTrustedRegistry(name))
384+
}
385+
386+
// Configure never-trusted registry from config
387+
if cfg.Registries.NeverTrusted != nil && cfg.Registries.NeverTrusted.Enabled {
388+
logger.Info("Configuring never-trusted registry from config")
389+
name := cfg.Registries.NeverTrusted.Name
390+
if name == "" {
391+
name = "never-trusted"
392+
}
393+
registryMgr.Register(static.NewNeverTrustedRegistry(name))
394+
}
395+
}
396+
222397
// splitCSV splits a comma-separated string and trims whitespace
223398
func splitCSV(s string) []string {
224399
if s == "" {

example/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ ETSI TSL options:
6363
--etsi-cert-bundle string Path to PEM file with trusted CA certificates
6464
--etsi-tsl-files string Comma-separated list of local TSL XML files
6565
66+
Whitelist/Static Registry options:
67+
--registry string Registry type: whitelist, always-trusted, never-trusted
68+
--whitelist string Path to whitelist YAML/JSON config file
69+
--whitelist-watch Watch whitelist file for changes (default: true)
70+
6671
Logging options:
6772
--log-level string debug, info, warn, error (default: info)
6873
--log-format string text or json (default: text)
6974
7075
Other:
71-
--config string Configuration file path (YAML format) - partial support
76+
--config string Configuration file path (YAML format)
7277
--help Show help message
7378
--version Show version information
7479
```

example/config.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
# This file documents all available configuration options.
44
# Use with: gt --config config.yaml
55
#
6-
# NOTE: The current command-line interface only supports a subset of these options.
7-
# Full config file support is planned. Currently supported via CLI:
8-
# --host, --port, --external-url, --etsi-cert-bundle, --etsi-tsl-files
9-
# --log-level, --log-format
6+
# Configuration file supports:
7+
# - server: host, port, external_url
8+
# - logging: level, format
9+
# - registries: etsi, whitelist, always_trusted, never_trusted
1010
#
11-
# This file serves as both documentation and a template for future config support.
11+
# CLI flags override config file values when both are specified.
12+
# Additional CLI registry options: --registry, --whitelist, --whitelist-watch
1213

1314
# =============================================================================
1415
# Server Configuration

pkg/api/api.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ func RegisterAPIRoutes(r *gin.Engine, serverCtx *ServerContext) {
275275
logging.F("burst", serverCtx.RateLimiter.burst))
276276
}
277277

278+
// Register health check endpoints (required for Kubernetes probes)
279+
RegisterHealthEndpoints(r, serverCtx)
280+
278281
// AuthZEN well-known discovery endpoint (Section 9 of base spec)
279282
r.GET("/.well-known/authzen-configuration", WellKnownHandler(serverCtx.BaseURL))
280283

pkg/config/config.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,54 @@ import (
1616
// Config represents the application configuration structure.
1717
// It includes settings for the server, logging, pipeline processing, and security.
1818
type Config struct {
19-
Server ServerConfig `yaml:"server"`
20-
Logging LoggingConfig `yaml:"logging"`
21-
Pipeline PipelineConfig `yaml:"pipeline"`
22-
Security SecurityConfig `yaml:"security"`
19+
Server ServerConfig `yaml:"server"`
20+
Logging LoggingConfig `yaml:"logging"`
21+
Pipeline PipelineConfig `yaml:"pipeline"`
22+
Security SecurityConfig `yaml:"security"`
23+
Registries RegistriesConfig `yaml:"registries"`
24+
}
25+
26+
// RegistriesConfig contains configuration for all trust registries.
27+
type RegistriesConfig struct {
28+
ETSI *ETSIRegistryConfig `yaml:"etsi,omitempty"`
29+
Whitelist *WhitelistRegistryConfig `yaml:"whitelist,omitempty"`
30+
// Static test registries
31+
AlwaysTrusted *StaticRegistryConfig `yaml:"always_trusted,omitempty"`
32+
NeverTrusted *StaticRegistryConfig `yaml:"never_trusted,omitempty"`
33+
}
34+
35+
// ETSIRegistryConfig contains ETSI TSL registry configuration.
36+
type ETSIRegistryConfig struct {
37+
Enabled bool `yaml:"enabled"`
38+
Name string `yaml:"name"`
39+
Description string `yaml:"description"`
40+
CertBundle string `yaml:"cert_bundle,omitempty"`
41+
TSLFiles []string `yaml:"tsl_files,omitempty"`
42+
TSLURLs []string `yaml:"tsl_urls,omitempty"`
43+
FollowRefs bool `yaml:"follow_refs"`
44+
MaxRefDepth int `yaml:"max_ref_depth"`
45+
AllowNetworkAccess bool `yaml:"allow_network_access"`
46+
FetchTimeout string `yaml:"fetch_timeout"`
47+
UserAgent string `yaml:"user_agent"`
48+
}
49+
50+
// WhitelistRegistryConfig contains whitelist registry configuration.
51+
type WhitelistRegistryConfig struct {
52+
Enabled bool `yaml:"enabled"`
53+
Name string `yaml:"name"`
54+
Description string `yaml:"description"`
55+
ConfigFile string `yaml:"config_file,omitempty"`
56+
WatchFile bool `yaml:"watch_file"`
57+
Issuers []string `yaml:"issuers,omitempty"`
58+
Verifiers []string `yaml:"verifiers,omitempty"`
59+
TrustedSubjects []string `yaml:"trusted_subjects,omitempty"`
60+
}
61+
62+
// StaticRegistryConfig contains static (always/never trusted) registry configuration.
63+
type StaticRegistryConfig struct {
64+
Enabled bool `yaml:"enabled"`
65+
Name string `yaml:"name"`
66+
Description string `yaml:"description"`
2367
}
2468

2569
// ServerConfig contains HTTP server configuration settings.

0 commit comments

Comments
 (0)