156 lines
2.9 KiB
Go
156 lines
2.9 KiB
Go
package gost
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Authenticator is an interface for user authentication.
|
|
type Authenticator interface {
|
|
Authenticate(user, password string) bool
|
|
}
|
|
|
|
// LocalAuthenticator is an Authenticator that authenticates client by local key-value pairs.
|
|
type LocalAuthenticator struct {
|
|
kvs map[string]string
|
|
period time.Duration
|
|
stopped chan struct{}
|
|
mux sync.RWMutex
|
|
}
|
|
|
|
// NewLocalAuthenticator creates an Authenticator that authenticates client by local infos.
|
|
func NewLocalAuthenticator(kvs map[string]string) *LocalAuthenticator {
|
|
return &LocalAuthenticator{
|
|
kvs: kvs,
|
|
stopped: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Authenticate checks the validity of the provided user-password pair.
|
|
func (au *LocalAuthenticator) Authenticate(user, password string) bool {
|
|
if au == nil {
|
|
return true
|
|
}
|
|
|
|
au.mux.RLock()
|
|
defer au.mux.RUnlock()
|
|
|
|
if len(au.kvs) == 0 {
|
|
return true
|
|
}
|
|
|
|
v, ok := au.kvs[user]
|
|
return ok && (v == "" || password == v)
|
|
}
|
|
|
|
// Add adds a key-value pair to the Authenticator.
|
|
func (au *LocalAuthenticator) Add(k, v string) {
|
|
au.mux.Lock()
|
|
defer au.mux.Unlock()
|
|
if au.kvs == nil {
|
|
au.kvs = make(map[string]string)
|
|
}
|
|
au.kvs[k] = v
|
|
}
|
|
|
|
// Reload parses config from r, then live reloads the Authenticator.
|
|
func (au *LocalAuthenticator) Reload(r io.Reader) error {
|
|
var period time.Duration
|
|
kvs := make(map[string]string)
|
|
|
|
if r == nil || au.Stopped() {
|
|
return nil
|
|
}
|
|
|
|
// splitLine splits a line text by white space.
|
|
// A line started with '#' will be ignored, otherwise it is valid.
|
|
split := func(line string) []string {
|
|
if line == "" {
|
|
return nil
|
|
}
|
|
line = strings.Replace(line, "\t", " ", -1)
|
|
line = strings.TrimSpace(line)
|
|
|
|
if strings.IndexByte(line, '#') == 0 {
|
|
return nil
|
|
}
|
|
|
|
var ss []string
|
|
for _, s := range strings.Split(line, " ") {
|
|
if s = strings.TrimSpace(s); s != "" {
|
|
ss = append(ss, s)
|
|
}
|
|
}
|
|
return ss
|
|
}
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
ss := split(line)
|
|
if len(ss) == 0 {
|
|
continue
|
|
}
|
|
|
|
switch ss[0] {
|
|
case "reload": // reload option
|
|
if len(ss) > 1 {
|
|
period, _ = time.ParseDuration(ss[1])
|
|
}
|
|
default:
|
|
var k, v string
|
|
k = ss[0]
|
|
if len(ss) > 1 {
|
|
v = ss[1]
|
|
}
|
|
kvs[k] = v
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
au.mux.Lock()
|
|
defer au.mux.Unlock()
|
|
|
|
au.period = period
|
|
au.kvs = kvs
|
|
|
|
return nil
|
|
}
|
|
|
|
// Period returns the reload period.
|
|
func (au *LocalAuthenticator) Period() time.Duration {
|
|
if au.Stopped() {
|
|
return -1
|
|
}
|
|
|
|
au.mux.RLock()
|
|
defer au.mux.RUnlock()
|
|
|
|
return au.period
|
|
}
|
|
|
|
// Stop stops reloading.
|
|
func (au *LocalAuthenticator) Stop() {
|
|
select {
|
|
case <-au.stopped:
|
|
default:
|
|
close(au.stopped)
|
|
}
|
|
}
|
|
|
|
// Stopped checks whether the reloader is stopped.
|
|
func (au *LocalAuthenticator) Stopped() bool {
|
|
select {
|
|
case <-au.stopped:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|