aboutsummaryrefslogtreecommitdiff
path: root/cmd/sklad/session.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/sklad/session.go')
-rw-r--r--cmd/sklad/session.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/cmd/sklad/session.go b/cmd/sklad/session.go
new file mode 100644
index 0000000..02fe0b0
--- /dev/null
+++ b/cmd/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 != "/" && r.Method == http.MethodGet {
+ 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)))
+ }
+}