feat: implement legacy config auth storage and core api
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PermMask uint8
|
||||
|
||||
const (
|
||||
@@ -12,8 +19,116 @@ const (
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user