Personal warehouse management system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
1.7KB

  1. package main
  2. import (
  3. "context"
  4. "encoding/hex"
  5. "math/rand"
  6. "net/http"
  7. "net/url"
  8. )
  9. // session storage indexed by a random UUID
  10. var sessions = map[string]*Session{}
  11. type Session struct {
  12. LoggedIn bool // may access the DB
  13. }
  14. type sessionContextKey struct{}
  15. func sessionGenId() string {
  16. u := make([]byte, 16)
  17. if _, err := rand.Read(u); err != nil {
  18. panic("cannot generate random bytes")
  19. }
  20. return hex.EncodeToString(u)
  21. }
  22. // TODO: We don't want to keep an unlimited amount of cookies in the storage.
  23. // - The essential question is: how do we avoid DoS?
  24. // - Which cookies are worth keeping?
  25. // - Definitely logged-in users, only one person should know the password.
  26. // - Evict by FIFO? LRU?
  27. func sessionGet(w http.ResponseWriter, r *http.Request) (session *Session) {
  28. if c, _ := r.Cookie("sessionid"); c != nil {
  29. session, _ = sessions[c.Value]
  30. }
  31. if session == nil {
  32. id := sessionGenId()
  33. session = &Session{LoggedIn: false}
  34. sessions[id] = session
  35. http.SetCookie(w, &http.Cookie{Name: "sessionid", Value: id})
  36. }
  37. return
  38. }
  39. func sessionWrap(inner func(http.ResponseWriter, *http.Request)) func(
  40. http.ResponseWriter, *http.Request) {
  41. return func(w http.ResponseWriter, r *http.Request) {
  42. // We might also try no-cache with an ETag for the whole database,
  43. // though I don't expect any substantial improvements of anything.
  44. w.Header().Set("Cache-Control", "no-store")
  45. redirect := "/login"
  46. if r.RequestURI != "/" && r.Method == http.MethodGet {
  47. redirect += "?redirect=" + url.QueryEscape(r.RequestURI)
  48. }
  49. session := sessionGet(w, r)
  50. if !session.LoggedIn {
  51. http.Redirect(w, r, redirect, http.StatusSeeOther)
  52. return
  53. }
  54. inner(w, r.WithContext(
  55. context.WithValue(r.Context(), sessionContextKey{}, session)))
  56. }
  57. }