summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.eu>2024-02-13 22:49:23 +0100
committerRené 'Necoro' Neumann <necoro@necoro.eu>2024-02-13 22:49:23 +0100
commit4c98ab6a3a1f41ebaa5360a6a4615cd705a94db0 (patch)
treee981302e500b18be87dbc3fe894627992783af15
parente9b709c12a9f64ab99b361a93c173548382147fd (diff)
downloadgosten-4c98ab6a3a1f41ebaa5360a6a4615cd705a94db0.tar.gz
gosten-4c98ab6a3a1f41ebaa5360a6a4615cd705a94db0.tar.bz2
gosten-4c98ab6a3a1f41ebaa5360a6a4615cd705a94db0.zip
session handling and login/logout
-rw-r--r--go.mod2
-rw-r--r--go.sum6
-rw-r--r--main.go104
3 files changed, 106 insertions, 6 deletions
diff --git a/go.mod b/go.mod
index b851d68..50c0149 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
github.com/go-sql-driver/mysql v1.7.1
github.com/gorilla/handlers v1.5.2
github.com/gorilla/schema v1.2.1
+ github.com/gorilla/sessions v1.2.2
github.com/mattn/go-sqlite3 v1.14.22
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551
golang.org/x/crypto v0.19.0
@@ -17,6 +18,7 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/google/uuid v1.3.0 // indirect
+ github.com/gorilla/securecookie v1.1.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
diff --git a/go.sum b/go.sum
index 6b6bff5..2b48e8d 100644
--- a/go.sum
+++ b/go.sum
@@ -10,6 +10,8 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -18,6 +20,10 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM=
github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
+github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
+github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
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}
+}