forked from ginuerzh/gost
-
Notifications
You must be signed in to change notification settings - Fork 1
/
auth.go
155 lines (130 loc) · 2.86 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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 bypass.
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
}
}