package auth import ( "crypto/rand" "crypto/sha256" "encoding/hex" "strings" ) type PermMask uint8 const ( None PermMask = 0 Read PermMask = 1 << iota Write Delete ) func (p PermMask) HasRead() bool { return p&Read != 0 } func (p PermMask) HasWrite() bool { return p&Write != 0 } func (p PermMask) HasDelete() bool { return p&Delete != 0 } func ParseMask(s string) PermMask { var p PermMask for _, r := range strings.ToLower(s) { switch r { case 'r': p |= Read case 'w': p |= Read | Write case 'd': p |= Read | Write | Delete } } return p } type PathRule struct { Dir string Mask PermMask } type Account struct { Username, Password string Mask PermMask Rules []PathRule } type Manager struct { Accounts map[string]*Account Guest *Account } func ParseRules(values []string) (*Manager, error) { m := &Manager{Accounts: map[string]*Account{}} for _, value := range values { for _, item := range strings.Split(value, "|") { if item == "" { continue } fields := strings.Split(item, ":") if len(fields) < 3 || len(fields)%2 == 0 { continue } a := &Account{Username: fields[0], Password: fields[1], Mask: ParseMask(fields[2])} for i := 3; i+1 < len(fields); i += 2 { a.Rules = append(a.Rules, PathRule{Dir: strings.Trim(fields[i], "/"), Mask: ParseMask(fields[i+1])}) } if a.Username == "" { m.Guest = a } else { m.Accounts[a.Username] = a } } } if m.Guest == nil { m.Guest = &Account{Mask: Read | Write} } return m, nil } func (m *Manager) Authenticate(user, pwd string) *Account { if a, ok := m.Accounts[user]; ok && a.Password == pwd { return a } if user == "" && pwd == "" { return m.Guest } return nil } func (a *Account) MaskFor(path string) PermMask { path = strings.Trim(path, "/") best := -1 mask := a.Mask for _, r := range a.Rules { d := strings.Trim(r.Dir, "/") if matchPath(d, path) && len(d) > best { best = len(d) mask = r.Mask } } return mask } func matchPath(pattern, path string) bool { if pattern == "" { return path == "" } pp := strings.Split(pattern, "/") ps := strings.Split(path, "/") if len(ps) < len(pp) { return false } for i, p := range pp { if p == "*" { return true } if p == "?" { if ps[i] == "" { return false } continue } if p != ps[i] { return false } } return true } func NewToken() string { b := make([]byte, 32) if _, e := rand.Read(b); e != nil { h := sha256.Sum256([]byte("fallback")) return hex.EncodeToString(h[:]) } return hex.EncodeToString(b) }