# HG changeset patch # User Sascha L. Teichmann # Date 1535116342 -7200 # Node ID 22e1bf563a0420c6423f628ff24f5c4880042345 # Parent 5c08afd15ce776163dd3c38c649eb7cfe1582669 Throw away the connection level for sessions. This is not needed any more because the db connection are not bound to the sessions any more. diff -r 5c08afd15ce7 -r 22e1bf563a04 pkg/auth/connection.go --- a/pkg/auth/connection.go Fri Aug 24 14:28:05 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -package auth - -import ( - "errors" - "io" - "sync" - "time" - - "gemma.intevation.de/gemma/pkg/misc" -) - -var ErrNoSuchToken = errors.New("No such token") - -type Connection struct { - session *Session - - access time.Time - - mu sync.Mutex -} - -func (c *Connection) serialize(w io.Writer) error { - if err := c.session.serialize(w); err != nil { - return err - } - access, err := c.last().MarshalText() - if err != nil { - return err - } - wr := misc.BinWriter{w, nil} - wr.WriteBin(uint32(len(access))) - wr.WriteBin(access) - return wr.Err -} - -func (c *Connection) deserialize(r io.Reader) error { - session := new(Session) - if err := session.deserialize(r); err != nil { - return err - } - - rd := misc.BinReader{r, nil} - var l uint32 - rd.ReadBin(&l) - access := make([]byte, l) - rd.ReadBin(access) - - if rd.Err != nil { - return rd.Err - } - - var t time.Time - if err := t.UnmarshalText(access); err != nil { - return err - } - - *c = Connection{ - session: session, - access: t, - } - - return nil -} - -func (c *Connection) set(session *Session) { - c.session = session - c.touch() -} - -func (c *Connection) touch() { - c.mu.Lock() - c.access = time.Now() - c.mu.Unlock() -} - -func (c *Connection) last() time.Time { - c.mu.Lock() - access := c.access - c.mu.Unlock() - return access -} diff -r 5c08afd15ce7 -r 22e1bf563a04 pkg/auth/session.go --- a/pkg/auth/session.go Fri Aug 24 14:28:05 2018 +0200 +++ b/pkg/auth/session.go Fri Aug 24 15:12:22 2018 +0200 @@ -4,6 +4,7 @@ "encoding/base64" "errors" "io" + "sync" "time" "gemma.intevation.de/gemma/pkg/common" @@ -16,6 +17,10 @@ ExpiresAt int64 `json:"expires"` User string `json:"user"` Roles Roles `json:"roles"` + + // private fields for managing expiration. + access time.Time + mu sync.Mutex } func (r Roles) Has(role string) bool { @@ -51,7 +56,7 @@ } } -func (s *Session) serialize(w io.Writer) error { +func (s *Session) serializePublic(w io.Writer) error { wr := misc.BinWriter{w, nil} wr.WriteBin(s.ExpiresAt) wr.WriteString(s.User) @@ -62,21 +67,76 @@ return wr.Err } +func (s *Session) serialize(w io.Writer) error { + + access, err := s.last().MarshalText() + if err != nil { + return err + } + + wr := misc.BinWriter{w, nil} + wr.WriteBin(s.ExpiresAt) + wr.WriteString(s.User) + wr.WriteBin(uint32(len(s.Roles))) + for _, role := range s.Roles { + wr.WriteString(role) + } + wr.WriteBin(uint32(len(access))) + wr.WriteBin(access) + return wr.Err +} + func (s *Session) deserialize(r io.Reader) error { - var x Session + + var session Session + var n uint32 rd := misc.BinReader{r, nil} - rd.ReadBin(&x.ExpiresAt) - rd.ReadString(&x.User) + rd.ReadBin(&session.ExpiresAt) + rd.ReadString(&session.User) rd.ReadBin(&n) - x.Roles = make(Roles, n) + session.Roles = make(Roles, n) + for i := uint32(0); n > 0 && i < n; i++ { - rd.ReadString(&x.Roles[i]) + rd.ReadString(&session.Roles[i]) + } + + if rd.Err != nil { + return rd.Err + } + + var l uint32 + rd.ReadBin(&l) + access := make([]byte, l) + rd.ReadBin(access) + + if rd.Err != nil { + return rd.Err + } + + var t time.Time + if err := t.UnmarshalText(access); err != nil { + return err } - if rd.Err == nil { - *s = x - } - return rd.Err + + session.access = t + + *s = session + + return nil +} + +func (c *Session) touch() { + c.mu.Lock() + c.access = time.Now() + c.mu.Unlock() +} + +func (c *Session) last() time.Time { + c.mu.Lock() + access := c.access + c.mu.Unlock() + return access } func GenerateSessionKey() string { diff -r 5c08afd15ce7 -r 22e1bf563a04 pkg/auth/store.go --- a/pkg/auth/store.go Fri Aug 24 14:28:05 2018 +0200 +++ b/pkg/auth/store.go Fri Aug 24 15:12:22 2018 +0200 @@ -2,19 +2,22 @@ import ( "bytes" + "errors" "log" "time" bolt "github.com/coreos/bbolt" ) +var ErrNoSuchToken = errors.New("No such token") + // Sessions is the global connection pool. var Sessions *SessionStore type SessionStore struct { - storage *bolt.DB - conns map[string]*Connection - cmds chan func(*SessionStore) + storage *bolt.DB + sessions map[string]*Session + cmds chan func(*SessionStore) } var sessionsBucket = []byte("sessions") @@ -22,8 +25,8 @@ func NewSessionStore(filename string) (*SessionStore, error) { pcp := &SessionStore{ - conns: make(map[string]*Connection), - cmds: make(chan func(*SessionStore)), + sessions: make(map[string]*Session), + cmds: make(chan func(*SessionStore)), } if err := pcp.openStorage(filename); err != nil { return nil, err @@ -55,11 +58,11 @@ c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { - var conn Connection - if err := conn.deserialize(bytes.NewReader(v)); err != nil { + var session Session + if err := session.deserialize(bytes.NewReader(v)); err != nil { return err } - pcp.conns[string(k)] = &conn + pcp.sessions[string(k)] = &session } return nil @@ -87,10 +90,10 @@ func (pcp *SessionStore) cleanToken() { now := time.Now() - for token, con := range pcp.conns { - expires := time.Unix(con.session.ExpiresAt, 0) + for token, session := range pcp.sessions { + expires := time.Unix(session.ExpiresAt, 0) if expires.Before(now) { - delete(pcp.conns, token) + delete(pcp.sessions, token) pcp.remove(token) } } @@ -112,25 +115,25 @@ func (pcp *SessionStore) Delete(token string) bool { res := make(chan bool) pcp.cmds <- func(pcp *SessionStore) { - if _, found := pcp.conns[token]; !found { + if _, found := pcp.sessions[token]; !found { res <- false return } - delete(pcp.conns, token) + delete(pcp.sessions, token) pcp.remove(token) res <- true } return <-res } -func (pcp *SessionStore) store(token string, con *Connection) { +func (pcp *SessionStore) store(token string, session *Session) { if pcp.storage == nil { return } err := pcp.storage.Update(func(tx *bolt.Tx) error { b := tx.Bucket(sessionsBucket) var buf bytes.Buffer - if err := con.serialize(&buf); err != nil { + if err := session.serialize(&buf); err != nil { return err } return b.Put([]byte(token), buf.Bytes()) @@ -140,22 +143,22 @@ } } -func (pcp *SessionStore) Add(token string, session *Session) *Connection { - res := make(chan *Connection) +func (pcp *SessionStore) Add(token string, session *Session) *Session { + res := make(chan *Session) pcp.cmds <- func(cp *SessionStore) { - con := pcp.conns[token] - if con == nil { - con = &Connection{} - pcp.conns[token] = con + s := pcp.sessions[token] + if s == nil { + s = session + pcp.sessions[token] = session } - con.set(session) - pcp.store(token, con) - res <- con + s.touch() + pcp.store(token, s) + res <- s } - con := <-res - return con + s := <-res + return s } func (pcp *SessionStore) Renew(token string) (string, error) { @@ -168,17 +171,17 @@ resCh := make(chan result) pcp.cmds <- func(cp *SessionStore) { - con := pcp.conns[token] - if con == nil { + session := pcp.sessions[token] + if session == nil { resCh <- result{err: ErrNoSuchToken} } else { - delete(pcp.conns, token) + delete(pcp.sessions, token) pcp.remove(token) newToken := GenerateSessionKey() // TODO: Ensure that this is not racy! - con.session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() - pcp.conns[newToken] = con - pcp.store(newToken, con) + session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() + pcp.sessions[newToken] = session + pcp.store(newToken, session) resCh <- result{newToken: newToken} } } @@ -187,46 +190,16 @@ return r.newToken, r.err } -func (pcp *SessionStore) Do(token string) (*Session, error) { - - type result struct { - session *Session - err error - } - - res := make(chan result) - - pcp.cmds <- func(pcp *SessionStore) { - con := pcp.conns[token] - if con == nil { - res <- result{err: ErrNoSuchToken} - return - } - con.touch() - pcp.store(token, con) - - res <- result{session: con.session} - } - - r := <-res - - if r.err != nil { - return nil, r.err - } - - return r.session, nil -} - func (pcp *SessionStore) Session(token string) *Session { res := make(chan *Session) pcp.cmds <- func(pcp *SessionStore) { - con := pcp.conns[token] - if con == nil { + session := pcp.sessions[token] + if session == nil { res <- nil } else { - con.touch() - pcp.store(token, con) - res <- con.session + session.touch() + pcp.store(token, session) + res <- session } } return <-res @@ -234,9 +207,9 @@ func (pcp *SessionStore) Logout(user string) { pcp.cmds <- func(pcp *SessionStore) { - for token, con := range pcp.conns { - if con.session.User == user { - delete(pcp.conns, token) + for token, session := range pcp.sessions { + if session.User == user { + delete(pcp.sessions, token) pcp.remove(token) } } diff -r 5c08afd15ce7 -r 22e1bf563a04 pkg/controllers/json.go --- a/pkg/controllers/json.go Fri Aug 24 14:28:05 2018 +0200 +++ b/pkg/controllers/json.go Fri Aug 24 15:12:22 2018 +0200 @@ -48,13 +48,14 @@ var err error if token, ok := auth.GetToken(req); ok && !j.NoConn { - var session *auth.Session - if session, err = auth.Sessions.Do(token); err != nil { + if session := auth.Sessions.Session(token); session != nil { var conn *sql.Conn if conn, err = auth.MetamorphConn(req.Context(), session.User); err != nil { defer conn.Close() jr, err = j.Handle(input, req, conn) } + } else { + err = auth.ErrNoSuchToken } } else { jr, err = j.Handle(input, req, nil)