Mercurial > gemma
view pkg/auth/store.go @ 616:51dc26b0f066
client: avoid extra password reveal button for IE/Edge
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Tue, 11 Sep 2018 09:50:37 +0200 |
parents | be8b79109679 |
children | a244b18cb916 |
line wrap: on
line source
package auth import ( "bytes" "errors" "log" "time" bolt "github.com/etcd-io/bbolt" ) var ErrNoSuchToken = errors.New("No such token") // Sessions is the global connection pool. var Sessions *SessionStore type SessionStore struct { storage *bolt.DB sessions map[string]*Session cmds chan func() } var sessionsBucket = []byte("sessions") func NewSessionStore(filename string) (*SessionStore, error) { ss := &SessionStore{ sessions: make(map[string]*Session), cmds: make(chan func()), } if err := ss.openStorage(filename); err != nil { return nil, err } go ss.run() return ss, nil } // openStorage opens a storage file. func (ss *SessionStore) openStorage(filename string) error { // No file, nothing to restore/persist. if filename == "" { return nil } db, err := bolt.Open(filename, 0600, nil) if err != nil { return err } err = db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucketIfNotExists(sessionsBucket) if err != nil { return err } // pre-load sessions c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { var session Session if err := session.deserialize(bytes.NewReader(v)); err != nil { return err } ss.sessions[string(k)] = &session } return nil }) if err != nil { db.Close() return err } ss.storage = db return nil } func (ss *SessionStore) run() { for { select { case cmd := <-ss.cmds: cmd() case <-time.After(time.Minute * 5): ss.cleanToken() } } } func (ss *SessionStore) cleanToken() { now := time.Now() for token, session := range ss.sessions { expires := time.Unix(session.ExpiresAt, 0) if expires.Before(now) { delete(ss.sessions, token) ss.remove(token) } } } func (ss *SessionStore) remove(token string) { if ss.storage == nil { return } err := ss.storage.Update(func(tx *bolt.Tx) error { b := tx.Bucket(sessionsBucket) return b.Delete([]byte(token)) }) if err != nil { log.Printf("error: %v\n", err) } } func (ss *SessionStore) Delete(token string) bool { res := make(chan bool) ss.cmds <- func() { if _, found := ss.sessions[token]; !found { res <- false return } delete(ss.sessions, token) ss.remove(token) res <- true } return <-res } func (ss *SessionStore) store(token string, session *Session) { if ss.storage == nil { return } err := ss.storage.Update(func(tx *bolt.Tx) error { b := tx.Bucket(sessionsBucket) var buf bytes.Buffer if err := session.serialize(&buf); err != nil { return err } return b.Put([]byte(token), buf.Bytes()) }) if err != nil { log.Printf("error: %v\n", err) } } func (ss *SessionStore) Add(token string, session *Session) { res := make(chan struct{}) ss.cmds <- func() { defer close(res) s := ss.sessions[token] if s == nil { s = session ss.sessions[token] = session } s.touch() ss.store(token, s) } <-res } func (ss *SessionStore) Renew(token string) (string, error) { type result struct { newToken string err error } resCh := make(chan result) ss.cmds <- func() { session := ss.sessions[token] if session == nil { resCh <- result{err: ErrNoSuchToken} } else { delete(ss.sessions, token) ss.remove(token) newToken := GenerateSessionKey() // TODO: Ensure that this is not racy! session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() ss.sessions[newToken] = session ss.store(newToken, session) resCh <- result{newToken: newToken} } } r := <-resCh return r.newToken, r.err } func (ss *SessionStore) Session(token string) *Session { res := make(chan *Session) ss.cmds <- func() { session := ss.sessions[token] if session == nil { res <- nil } else { session.touch() ss.store(token, session) res <- session } } return <-res } func (ss *SessionStore) Logout(user string) { ss.cmds <- func() { for token, session := range ss.sessions { if session.User == user { delete(ss.sessions, token) ss.remove(token) } } } } func (ss *SessionStore) Shutdown() error { if db := ss.storage; db != nil { log.Println("info: shutdown persistent session store.") ss.storage = nil return db.Close() } log.Println("info: shutdown in-memory session store.") return nil }