k_card/component3/gated.go

65 lines
1.4 KiB
Go

package main
import (
"bufio"
"os"
"strings"
"sync"
)
// GatedHosts is the set of hostnames that require FIDO2 authentication.
// Format matches k_phone's gated_hosts.txt: one "host" or "host:port" per line,
// lines starting with "#" and blank lines are ignored.
type GatedHosts struct {
mu sync.RWMutex
entries map[string]bool
}
// Load reads the gated hosts file. Missing file is not an error (empty list).
func (g *GatedHosts) Load(path string) error {
f, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer f.Close()
entries := make(map[string]bool)
sc := bufio.NewScanner(f)
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
// Normalise: lowercase, strip any trailing port-free colon.
entries[strings.ToLower(line)] = true
}
g.mu.Lock()
g.entries = entries
g.mu.Unlock()
return sc.Err()
}
// Len returns the number of entries in the gated list.
func (g *GatedHosts) Len() int {
g.mu.RLock()
defer g.mu.RUnlock()
return len(g.entries)
}
// IsGated returns true if host:port matches a gated entry.
// An entry "example.com" matches any port; "example.com:8080" matches only port 8080.
func (g *GatedHosts) IsGated(host, port string) bool {
g.mu.RLock()
defer g.mu.RUnlock()
if len(g.entries) == 0 {
return false
}
h := strings.ToLower(host)
return g.entries[h] || (port != "" && g.entries[h+":"+port])
}