changeset 498:22e1bf563a04 metamorph-for-all

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.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 24 Aug 2018 15:12:22 +0200
parents 5c08afd15ce7
children c10c76c92797
files pkg/auth/connection.go pkg/auth/session.go pkg/auth/store.go pkg/controllers/json.go
diffstat 4 files changed, 116 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- 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
-}
--- 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 {
--- 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)
 			}
 		}
--- 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)