diff options
author | Přemysl Janouch <p@janouch.name> | 2019-04-13 22:50:08 +0200 |
---|---|---|
committer | Přemysl Janouch <p@janouch.name> | 2019-04-13 23:54:24 +0200 |
commit | bcfb9fbc2bc035149a71df1ffef96f23e668e815 (patch) | |
tree | 3a2cad243a1f729cd212fd0432ddbfe7e1362a57 /sklad/session.go | |
parent | f5790dbff9cec6d3fbd09952ba5453ef7a13baf9 (diff) | |
download | sklad-bcfb9fbc2bc035149a71df1ffef96f23e668e815.tar.gz sklad-bcfb9fbc2bc035149a71df1ffef96f23e668e815.tar.xz sklad-bcfb9fbc2bc035149a71df1ffef96f23e668e815.zip |
sklad: implement login and logout
So far there are no other pages, and nothing links to the logout.
Diffstat (limited to 'sklad/session.go')
-rw-r--r-- | sklad/session.go | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/sklad/session.go b/sklad/session.go new file mode 100644 index 0000000..0d0686a --- /dev/null +++ b/sklad/session.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "encoding/hex" + "math/rand" + "net/http" + "net/url" +) + +// session storage indexed by a random UUID +var sessions = map[string]*Session{} + +type Session struct { + LoggedIn bool // may access the DB +} + +type sessionContextKey struct{} + +func sessionGenId() string { + u := make([]byte, 16) + if _, err := rand.Read(u); err != nil { + panic("cannot generate random bytes") + } + return hex.EncodeToString(u) +} + +// TODO: We don't want to keep an unlimited amount of cookies in the storage. +// - The essential question is: how do we avoid DoS? +// - Which cookies are worth keeping? +// - Definitely logged-in users, only one person should know the password. +// - Evict by FIFO? LRU? +func sessionGet(w http.ResponseWriter, r *http.Request) (session *Session) { + if c, _ := r.Cookie("sessionid"); c != nil { + session, _ = sessions[c.Value] + } + if session == nil { + id := sessionGenId() + session = &Session{LoggedIn: false} + sessions[id] = session + http.SetCookie(w, &http.Cookie{Name: "sessionid", Value: id}) + } + return +} + +func sessionWrap(inner func(http.ResponseWriter, *http.Request)) func( + http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + // We might also try no-cache with an ETag for the whole database, + // though I don't expect any substantial improvements of anything. + w.Header().Set("Cache-Control", "no-store") + + redirect := "/login" + if r.RequestURI != "/" { + redirect += "?redirect=" + url.QueryEscape(r.RequestURI) + } + + session := sessionGet(w, r) + if !session.LoggedIn { + http.Redirect(w, r, redirect, http.StatusSeeOther) + return + } + inner(w, r.WithContext( + context.WithValue(r.Context(), sessionContextKey{}, session))) + } +} |