// Package access evaluates reusable access lists: CIDR allow/deny rules // plus an optional HTTP Basic Auth user set. package access import ( "net" "sort" "github.com/owen/vibeproxy/internal/models" ) type rule struct { allow bool net *net.IPNet prio int } // List is the runtime-evaluatable form of a models.AccessList. type List struct { ID int64 Name string DefaultAllow bool rules []rule Users map[string]string // username -> argon2id hash } func Compile(m models.AccessList) *List { l := &List{ID: m.ID, Name: m.Name, DefaultAllow: m.DefaultAllow, Users: map[string]string{}} for _, r := range m.Rules { _, ipnet, err := net.ParseCIDR(r.CIDR) if err != nil { continue } l.rules = append(l.rules, rule{allow: r.Action == models.AccessAllow, net: ipnet, prio: r.Priority}) } sort.SliceStable(l.rules, func(i, j int) bool { return l.rules[i].prio < l.rules[j].prio }) for _, u := range m.BasicAuthUsers { l.Users[u.Username] = u.PasswordHash } return l } // Allowed reports whether ip is permitted by this list's CIDR rules. The // first matching rule (in priority order) wins; otherwise the default // action applies. func (l *List) Allowed(ip net.IP) bool { for _, r := range l.rules { if r.net.Contains(ip) { return r.allow } } return l.DefaultAllow } // RequiresAuth reports whether this list has HTTP Basic Auth users // configured, meaning IP-allowed requests must still authenticate. func (l *List) RequiresAuth() bool { return len(l.Users) > 0 }