From 4c98ab6a3a1f41ebaa5360a6a4615cd705a94db0 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Tue, 13 Feb 2024 22:49:23 +0100 Subject: session handling and login/logout --- main.go | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 6 deletions(-) (limited to 'main.go') diff --git a/main.go b/main.go index 863e1ff..ef6cef3 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,9 @@ package main import ( + "context" "database/sql" + "encoding/gob" "errors" "flag" "fmt" @@ -13,6 +15,8 @@ import ( "github.com/gorilla/handlers" "github.com/gorilla/schema" + "github.com/gorilla/securecookie" + "github.com/gorilla/sessions" "golang.org/x/crypto/bcrypt" "gosten/model" @@ -28,10 +32,15 @@ var ( func init() { flag.StringVar(&host, "h", "localhost", "address to listen on") flag.Uint64Var(&port, "p", 8080, "port to listen on") + + gob.Register(SessionData{}) } var Q *model.Queries var s *schema.Decoder +var sessionStore sessions.Store + +const sessionCookie = "sessionKeks" func main() { flag.Parse() @@ -44,14 +53,17 @@ func main() { Q = model.New(db) s = schema.NewDecoder() + sessionStore = sessions.NewCookieStore(securecookie.GenerateRandomKey(32)) mux := http.NewServeMux() - mux.HandleFunc("/{$}", showTemplate("index", nil)) - mux.HandleFunc("GET /login", showTemplate("login", User{})) + mux.Handle("/{$}", showTemplate("index", nil)) + mux.HandleFunc("GET /login", loginPage) mux.HandleFunc("POST /login", handleLogin) + mux.HandleFunc("GET /logout", handleLogout) - handler := handlers.CombinedLoggingHandler(os.Stderr, mux) + handler := sessionHandler(mux) + handler = handlers.CombinedLoggingHandler(os.Stderr, handler) handler = handlers.ProxyHeaders(handler) address := net.JoinHostPort(host, strconv.FormatUint(port, 10)) @@ -59,9 +71,10 @@ func main() { } type User struct { - Name string `form:"options=required"` - Password string `form:"type=password;options=required"` - Errors []error `form:"-"` + Name string `form:"options=required"` + Password string `form:"type=password;options=required"` + RememberMe bool `form:"type=checkbox;value=y;options=checked"` + Errors []error `form:"-"` } type fieldError struct { @@ -94,6 +107,24 @@ func parseForm[T any](r *http.Request, data *T) { } } +func loginPage(w http.ResponseWriter, r *http.Request) { + s := session(r) + + if !s.s.IsNew && s.Authenticated { + u, err := Q.GetUserById(r.Context(), s.UserID) + if err != nil { + s.Authenticated = false + s.Save(w, r) + } else { + u2 := User{Name: u.Name} + showTemplate("login2", u2).ServeHTTP(w, r) + return + } + } + + showTemplate("login", User{}).ServeHTTP(w, r) +} + func handleLogin(w http.ResponseWriter, r *http.Request) { u := User{} parseForm(r, &u) @@ -120,5 +151,66 @@ func handleLogin(w http.ResponseWriter, r *http.Request) { return } + s := session(r) + if u.RememberMe { + s.MaxAge(86400 * 7) // 1 week + } else { + s.MaxAge(0) + } + + s.UserID = dbUser.ID + s.Authenticated = true + s.Save(w, r) + showTemplate("login2", u).ServeHTTP(w, r) } + +func handleLogout(w http.ResponseWriter, r *http.Request) { + s := session(r) + s.Authenticated = false + s.MaxAge(-1) + s.Save(w, r) +} + +func sessionHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + session, err := sessionStore.Get(r, sessionCookie) + if err != nil { + } + + ctx := context.WithValue(r.Context(), "_session", session) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +type Session struct { + *SessionData + s *sessions.Session +} + +type SessionData struct { + UserID int64 + Authenticated bool +} + +func (s *Session) Save(w http.ResponseWriter, r *http.Request) { + s.s.Values["data"] = *s.SessionData + if err := s.s.Save(r, w); err != nil { + log.Panic("Storing session: ", err) + } +} + +func (s *Session) MaxAge(maxAge int) { + s.s.Options.MaxAge = maxAge +} + +func session(r *http.Request) Session { + s := r.Context().Value("_session").(*sessions.Session) + s.Options.HttpOnly = true + + sd, ok := s.Values["data"].(SessionData) + if !ok { + sd = SessionData{} + } + return Session{&sd, s} +} -- cgit v1.2.3-70-g09d2