feat(meshmapper): accept scope sources outside configured regions

This commit is contained in:
MrAlders0n committed 2026-09-30 22:27:36 -04:00
1 parent 9a4e17b270
commit 7267b8bca3
4 files changed
+15 -14

No files matched your search

+2 -2
View File
@@ -194,8 +194,8 @@ even when the same bearer-authenticated request works with curl.
```yaml
# MeshMapper integrations (optional, no API key). Import transport scopes and
# IATA border outlines from MeshMapper instead of maintaining scopes: and
# iatas.*.borderFile by hand. Scope sources must be in a configured region;
# zones covers every known IATA. See config.yaml.example for limits and behaviour.
# iatas.*.borderFile by hand. Neither needs the IATAs to be in a configured
# region. See config.yaml.example for limits and behaviour.
#meshmapper:
# scopes:
# enabled: true
+2 -2
View File
@@ -43,10 +43,10 @@ ratelimit:
# MeshMapper integrations (optional, no API key). Import transport scopes and
# IATA border outlines from MeshMapper instead of maintaining scopes: and
# iatas.*.borderFile by hand. Both need the IATAs to be in a configured region below.
# iatas.*.borderFile by hand.
#
# Scope-name discovery. Manual scopes remain authoritative.
# Sources must belong to a configured region's IATAs.
# Sources are keyed by IATA code; the IATA need not be in a configured region.
# Group endpoints are not supported: their names cannot be attributed to each IATA.
# At most 16 sources, 64 names per source, 64 KiB per response; no page scraping.
# Catalogues and ETags persist in PostgreSQL. Failed refreshes retain known names.
+7 -9
View File
@@ -45,7 +45,10 @@ func (c MeshMapperScopesConfig) Interval() time.Duration {
return c.RefreshInterval.Duration
}
var meshMapperHost = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*\.meshmapper\.net$`)
var (
meshMapperHost = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*\.meshmapper\.net$`)
iataCode = regexp.MustCompile(`^[A-Z]{3}$`)
)
func (c *Config) validateMeshMapper() error {
if z := c.MeshMapper.Zones; z.Enabled {
@@ -64,15 +67,10 @@ func (c *Config) validateMeshMapper() error {
if s.Interval() < 5*time.Minute || s.Interval() > 24*time.Hour {
return fmt.Errorf("meshmapper.scopes.refresh_interval must be between 5m and 24h")
}
configured := map[string]bool{}
for _, region := range c.Regions {
for _, iata := range region.IATAs {
configured[iata] = true
}
}
for iata, endpoint := range s.Sources {
if !configured[iata] {
return fmt.Errorf("meshmapper.scopes source %q must belong to a configured region", iata)
// Catalogues must name the source IATA exactly, so reject keys that could never match.
if !iataCode.MatchString(iata) {
return fmt.Errorf("meshmapper.scopes source %q must be a three-letter uppercase IATA code", iata)
}
u, err := url.Parse(endpoint)
if err != nil || u.Scheme != "https" || u.User != nil || !meshMapperHost.MatchString(u.Host) ||
+4 -1
View File
@@ -18,7 +18,10 @@ func TestMeshMapperConfig(t *testing.T) {
bad bool
}{
{"valid", valid, false},
{"unknown region", strings.Replace(valid, "iatas: [YOW]", "iatas: [YVR]", 1), true},
{"outside any region", strings.Replace(valid, "iatas: [YOW]", "iatas: [YVR]", 1), false},
{"no regions", valid[strings.Index(valid, "meshmapper:"):], false},
{"lowercase IATA", strings.Replace(valid, " YOW:", " yow:", 1), true},
{"long IATA", strings.Replace(valid, " YOW:", " YOWX:", 1), true},
{"http", strings.Replace(valid, "https:", "http:", 1), true},
{"private host", strings.Replace(valid, "yow.meshmapper.net", "127.0.0.1", 1), true},
{"credentials", strings.Replace(valid, "https://", "https://secret@", 1), true},