Mercurial > gemma
comparison pkg/auth/store.go @ 495:5c08afd15ce7 metamorph-for-all
Give file containing the session store a more suited name.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Fri, 24 Aug 2018 14:28:05 +0200 |
parents | pkg/auth/pool.go@8a0737aa6ab6 |
children | 22e1bf563a04 |
comparison
equal
deleted
inserted
replaced
493:8a0737aa6ab6 | 495:5c08afd15ce7 |
---|---|
1 package auth | |
2 | |
3 import ( | |
4 "bytes" | |
5 "log" | |
6 "time" | |
7 | |
8 bolt "github.com/coreos/bbolt" | |
9 ) | |
10 | |
11 // Sessions is the global connection pool. | |
12 var Sessions *SessionStore | |
13 | |
14 type SessionStore struct { | |
15 storage *bolt.DB | |
16 conns map[string]*Connection | |
17 cmds chan func(*SessionStore) | |
18 } | |
19 | |
20 var sessionsBucket = []byte("sessions") | |
21 | |
22 func NewSessionStore(filename string) (*SessionStore, error) { | |
23 | |
24 pcp := &SessionStore{ | |
25 conns: make(map[string]*Connection), | |
26 cmds: make(chan func(*SessionStore)), | |
27 } | |
28 if err := pcp.openStorage(filename); err != nil { | |
29 return nil, err | |
30 } | |
31 go pcp.run() | |
32 return pcp, nil | |
33 } | |
34 | |
35 // openStorage opens a storage file. | |
36 func (pcp *SessionStore) openStorage(filename string) error { | |
37 | |
38 // No file, nothing to restore/persist. | |
39 if filename == "" { | |
40 return nil | |
41 } | |
42 | |
43 db, err := bolt.Open(filename, 0600, nil) | |
44 if err != nil { | |
45 return err | |
46 } | |
47 | |
48 err = db.Update(func(tx *bolt.Tx) error { | |
49 b, err := tx.CreateBucketIfNotExists(sessionsBucket) | |
50 if err != nil { | |
51 return err | |
52 } | |
53 | |
54 // pre-load sessions | |
55 c := b.Cursor() | |
56 | |
57 for k, v := c.First(); k != nil; k, v = c.Next() { | |
58 var conn Connection | |
59 if err := conn.deserialize(bytes.NewReader(v)); err != nil { | |
60 return err | |
61 } | |
62 pcp.conns[string(k)] = &conn | |
63 } | |
64 | |
65 return nil | |
66 }) | |
67 | |
68 if err != nil { | |
69 db.Close() | |
70 return err | |
71 } | |
72 | |
73 pcp.storage = db | |
74 return nil | |
75 } | |
76 | |
77 func (pcp *SessionStore) run() { | |
78 for { | |
79 select { | |
80 case cmd := <-pcp.cmds: | |
81 cmd(pcp) | |
82 case <-time.After(time.Minute * 5): | |
83 pcp.cleanToken() | |
84 } | |
85 } | |
86 } | |
87 | |
88 func (pcp *SessionStore) cleanToken() { | |
89 now := time.Now() | |
90 for token, con := range pcp.conns { | |
91 expires := time.Unix(con.session.ExpiresAt, 0) | |
92 if expires.Before(now) { | |
93 delete(pcp.conns, token) | |
94 pcp.remove(token) | |
95 } | |
96 } | |
97 } | |
98 | |
99 func (pcp *SessionStore) remove(token string) { | |
100 if pcp.storage == nil { | |
101 return | |
102 } | |
103 err := pcp.storage.Update(func(tx *bolt.Tx) error { | |
104 b := tx.Bucket(sessionsBucket) | |
105 return b.Delete([]byte(token)) | |
106 }) | |
107 if err != nil { | |
108 log.Printf("error: %v\n", err) | |
109 } | |
110 } | |
111 | |
112 func (pcp *SessionStore) Delete(token string) bool { | |
113 res := make(chan bool) | |
114 pcp.cmds <- func(pcp *SessionStore) { | |
115 if _, found := pcp.conns[token]; !found { | |
116 res <- false | |
117 return | |
118 } | |
119 delete(pcp.conns, token) | |
120 pcp.remove(token) | |
121 res <- true | |
122 } | |
123 return <-res | |
124 } | |
125 | |
126 func (pcp *SessionStore) store(token string, con *Connection) { | |
127 if pcp.storage == nil { | |
128 return | |
129 } | |
130 err := pcp.storage.Update(func(tx *bolt.Tx) error { | |
131 b := tx.Bucket(sessionsBucket) | |
132 var buf bytes.Buffer | |
133 if err := con.serialize(&buf); err != nil { | |
134 return err | |
135 } | |
136 return b.Put([]byte(token), buf.Bytes()) | |
137 }) | |
138 if err != nil { | |
139 log.Printf("error: %v\n", err) | |
140 } | |
141 } | |
142 | |
143 func (pcp *SessionStore) Add(token string, session *Session) *Connection { | |
144 res := make(chan *Connection) | |
145 | |
146 pcp.cmds <- func(cp *SessionStore) { | |
147 con := pcp.conns[token] | |
148 if con == nil { | |
149 con = &Connection{} | |
150 pcp.conns[token] = con | |
151 } | |
152 con.set(session) | |
153 pcp.store(token, con) | |
154 res <- con | |
155 } | |
156 | |
157 con := <-res | |
158 return con | |
159 } | |
160 | |
161 func (pcp *SessionStore) Renew(token string) (string, error) { | |
162 | |
163 type result struct { | |
164 newToken string | |
165 err error | |
166 } | |
167 | |
168 resCh := make(chan result) | |
169 | |
170 pcp.cmds <- func(cp *SessionStore) { | |
171 con := pcp.conns[token] | |
172 if con == nil { | |
173 resCh <- result{err: ErrNoSuchToken} | |
174 } else { | |
175 delete(pcp.conns, token) | |
176 pcp.remove(token) | |
177 newToken := GenerateSessionKey() | |
178 // TODO: Ensure that this is not racy! | |
179 con.session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() | |
180 pcp.conns[newToken] = con | |
181 pcp.store(newToken, con) | |
182 resCh <- result{newToken: newToken} | |
183 } | |
184 } | |
185 | |
186 r := <-resCh | |
187 return r.newToken, r.err | |
188 } | |
189 | |
190 func (pcp *SessionStore) Do(token string) (*Session, error) { | |
191 | |
192 type result struct { | |
193 session *Session | |
194 err error | |
195 } | |
196 | |
197 res := make(chan result) | |
198 | |
199 pcp.cmds <- func(pcp *SessionStore) { | |
200 con := pcp.conns[token] | |
201 if con == nil { | |
202 res <- result{err: ErrNoSuchToken} | |
203 return | |
204 } | |
205 con.touch() | |
206 pcp.store(token, con) | |
207 | |
208 res <- result{session: con.session} | |
209 } | |
210 | |
211 r := <-res | |
212 | |
213 if r.err != nil { | |
214 return nil, r.err | |
215 } | |
216 | |
217 return r.session, nil | |
218 } | |
219 | |
220 func (pcp *SessionStore) Session(token string) *Session { | |
221 res := make(chan *Session) | |
222 pcp.cmds <- func(pcp *SessionStore) { | |
223 con := pcp.conns[token] | |
224 if con == nil { | |
225 res <- nil | |
226 } else { | |
227 con.touch() | |
228 pcp.store(token, con) | |
229 res <- con.session | |
230 } | |
231 } | |
232 return <-res | |
233 } | |
234 | |
235 func (pcp *SessionStore) Logout(user string) { | |
236 pcp.cmds <- func(pcp *SessionStore) { | |
237 for token, con := range pcp.conns { | |
238 if con.session.User == user { | |
239 delete(pcp.conns, token) | |
240 pcp.remove(token) | |
241 } | |
242 } | |
243 } | |
244 } | |
245 | |
246 func (pcp *SessionStore) Shutdown() error { | |
247 if db := pcp.storage; db != nil { | |
248 log.Println("info: shutdown persistent connection pool.") | |
249 pcp.storage = nil | |
250 return db.Close() | |
251 } | |
252 log.Println("info: shutdown in-memory connection pool.") | |
253 return nil | |
254 } |