# HG changeset patch # User Sascha L. Teichmann # Date 1532104331 -7200 # Node ID 1585c334e8a70e5c6b1156ebe31dcb13507bc0f2 # Parent 3457a60fb12d06ef379d4f14c198ff3d05a2c962 More on persisting sessions. diff -r 3457a60fb12d -r 1585c334e8a7 auth/connection.go --- a/auth/connection.go Fri Jul 20 17:11:57 2018 +0200 +++ b/auth/connection.go Fri Jul 20 18:32:11 2018 +0200 @@ -1,8 +1,11 @@ package auth import ( + "bytes" "database/sql" + "encoding/binary" "errors" + "io" "log" "sync" "time" @@ -47,6 +50,38 @@ mu sync.Mutex } +func (c *Connection) serialize() []byte { + var buf bytes.Buffer + c.session.serialize(&buf) + access, _ := c.last().MarshalText() + binary.Write(&buf, binary.BigEndian, string(access)) + return buf.Bytes() +} + +func (c *Connection) unserialize(r io.Reader) error { + session := new(Session) + if err := session.unserialize(r); err != nil { + return err + } + + var access string + if err := binary.Read(r, binary.BigEndian, &access); err != nil { + return err + } + + var t time.Time + if err := t.UnmarshalText([]byte(access)); err != nil { + return err + } + + *c = Connection{ + session: session, + access: t, + } + + return nil +} + func (c *Connection) set(session *Session) { c.session = session c.touch() diff -r 3457a60fb12d -r 1585c334e8a7 auth/persistent.go --- a/auth/persistent.go Fri Jul 20 17:11:57 2018 +0200 +++ b/auth/persistent.go Fri Jul 20 18:32:11 2018 +0200 @@ -1,26 +1,105 @@ package auth import ( + "bytes" "database/sql" "log" + "time" + + bolt "github.com/coreos/bbolt" ) type PersistentConnectionPool struct { - filename string + db *bolt.DB + conns map[string]*Connection + cmds chan func(*PersistentConnectionPool) } +var sessionsBucket = []byte("sessions") + func NewPersistentConnectionPool(filename string) (*PersistentConnectionPool, error) { - log.Println("NewInMemoryConnectionPool: Not implemented, yet.") + db, err := bolt.Open(filename, 0600, nil) + if err != nil { + return nil, err + } + + conns := make(map[string]*Connection) + 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 conn Connection + if err := conn.unserialize(bytes.NewReader(v)); err != nil { + return err + } + conns[string(k)] = &conn + } + + return nil + }) + + if err != nil { + db.Close() + return nil, err + } + pcp := &PersistentConnectionPool{ - filename: filename, + db: db, + conns: conns, + cmds: make(chan func(*PersistentConnectionPool)), } + go pcp.run() return pcp, nil } +func (pcp *PersistentConnectionPool) run() { + for { + select { + case cmd := <-pcp.cmds: + cmd(pcp) + case <-time.After(time.Minute): + pcp.cleanDB() + case <-time.After(time.Minute * 5): + pcp.cleanToken() + } + } +} + +func (pcp *PersistentConnectionPool) cleanDB() { + log.Println("cleanDB: Not implemented, yet.") +} + +func (pcp *PersistentConnectionPool) cleanToken() { + log.Println("cleanToken: Not implemented, yet.") +} + func (pcp *PersistentConnectionPool) Delete(token string) bool { - log.Println("Delete: Not implemented, yet.") - return false + res := make(chan bool) + pcp.cmds <- func(pcp *PersistentConnectionPool) { + conn, found := pcp.conns[token] + if !found { + res <- false + return + } + conn.close() + delete(pcp.conns, token) + err := pcp.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(sessionsBucket) + return b.Delete([]byte(token)) + }) + if err != nil { + log.Printf("error: %v\n", err) + } + res <- true + } + return <-res } func (pcp *PersistentConnectionPool) Add(token string, session *Session) *Connection { @@ -44,6 +123,10 @@ } func (pcp *PersistentConnectionPool) Shutdown() error { - log.Println("Shutdown: Not implemented, yet.") + log.Println("info: shutdown persistent connection pool.") + if db := pcp.db; db != nil { + pcp.db = nil + return db.Close() + } return nil } diff -r 3457a60fb12d -r 1585c334e8a7 auth/session.go --- a/auth/session.go Fri Jul 20 17:11:57 2018 +0200 +++ b/auth/session.go Fri Jul 20 18:32:11 2018 +0200 @@ -3,6 +3,7 @@ import ( "crypto/rand" "encoding/base64" + "encoding/binary" "io" "time" ) @@ -30,6 +31,33 @@ } } +func (s *Session) serialize(w io.Writer) { + binary.Write(w, binary.BigEndian, s.ExpiresAt) + binary.Write(w, binary.BigEndian, s.User) + binary.Write(w, binary.BigEndian, s.Password) + binary.Write(w, binary.BigEndian, uint32(len(s.Roles))) + for _, role := range s.Roles { + binary.Write(w, binary.BigEndian, role) + } +} + +func (s *Session) unserialize(r io.Reader) error { + var x Session + binary.Read(r, binary.BigEndian, &x.ExpiresAt) + binary.Read(r, binary.BigEndian, &x.User) + binary.Read(r, binary.BigEndian, &x.Password) + var n uint32 + binary.Read(r, binary.BigEndian, &n) + x.Roles = make([]string, n) + for i := uint32(0); n > 0 && i < n; i++ { + if err := binary.Read(r, binary.BigEndian, &x.Roles[i]); err != nil { + return err + } + } + *s = x + return nil +} + func GenerateSessionKey() string { return base64.URLEncoding.EncodeToString(GenerateRandomKey(sessionKeyLength)) }