Files
vibeproxy/internal/access/access.go
T
owenandClaude Sonnet 5 ba1f7c4c03 Initial vibeproxy implementation
Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go,
with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS
framework), and a REST API under /api/v1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
2026-08-17 10:28:59 -05:00

58 lines
1.5 KiB
Go

// 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 }