github.com/yaling888/clash@v1.53.0/component/auth/auth.go (about) 1 package auth 2 3 import ( 4 "crypto/subtle" 5 "sync" 6 7 "github.com/samber/lo" 8 ) 9 10 type Authenticator interface { 11 Verify(user []byte, pass []byte) bool 12 HasUser(user []byte) bool 13 Users() []string 14 RandomUser() *AuthUser 15 } 16 17 type AuthUser struct { 18 User string 19 Pass string 20 } 21 22 var _ Authenticator = (*inMemoryAuthenticator)(nil) 23 24 type inMemoryAuthenticator struct { 25 storage *sync.Map 26 usernames []string 27 } 28 29 func (au *inMemoryAuthenticator) Verify(user []byte, pass []byte) bool { 30 realPass, ok := au.storage.Load(string(user)) 31 return ok && subtle.ConstantTimeCompare(realPass.([]byte), pass) == 1 32 } 33 34 func (au *inMemoryAuthenticator) HasUser(user []byte) bool { 35 _, ok := au.storage.Load(string(user)) 36 return ok 37 } 38 39 func (au *inMemoryAuthenticator) Users() []string { 40 return au.usernames 41 } 42 43 func (au *inMemoryAuthenticator) RandomUser() *AuthUser { 44 if au == nil || len(au.usernames) == 0 { 45 return nil 46 } 47 48 user := lo.Sample(au.usernames) 49 realPass, ok := au.storage.Load(user) 50 if !ok { 51 return nil 52 } 53 54 return &AuthUser{ 55 User: user, 56 Pass: string(realPass.([]byte)), 57 } 58 } 59 60 func NewAuthenticator(users []AuthUser) Authenticator { 61 if len(users) == 0 { 62 return nil 63 } 64 65 au := &inMemoryAuthenticator{storage: &sync.Map{}} 66 usernames := make([]string, 0, len(users)) 67 for _, user := range users { 68 au.storage.Store(user.User, []byte(user.Pass)) 69 usernames = append(usernames, user.User) 70 } 71 au.usernames = lo.Uniq(usernames) 72 73 return au 74 }