diff --git a/cmd/iroon/main.go b/cmd/iroon/main.go index 112adc0..b8801d4 100644 --- a/cmd/iroon/main.go +++ b/cmd/iroon/main.go @@ -1,20 +1,25 @@ package iroon import ( + "fmt" "log" "net/http" "github.com/Bronku/iroon/internal/auth" "github.com/Bronku/iroon/internal/logging" "github.com/Bronku/iroon/internal/server" + "github.com/Bronku/iroon/internal/store" ) func Run() { - h := server.New() + s := store.OpenStore("./foo.db") + defer s.Close() + h := server.New(s) defer h.Close() var handler http.Handler = h handler = logging.Middleware(handler) - handler = auth.New().Middleware(handler) + handler = auth.New(s).Middleware(handler) + fmt.Println("starting server") log.Fatal(http.ListenAndServe(":8080", handler)) } diff --git a/internal/auth/auth.go b/internal/auth/auth.go index b17cd0e..08f1b81 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -3,22 +3,35 @@ package auth import ( _ "embed" "fmt" + "log" "net/http" "time" + + "github.com/Bronku/iroon/internal/models" + "github.com/Bronku/iroon/internal/store" ) -//go:embed login.html +//go:embed templates/login.html var loginPage string -//go:embed wrongPassword.html +//go:embed templates/wrongPassword.html var wrongPassword string type Authenticator struct { - sessions map[string]token + sessions map[string]models.Token + s *store.Store } -func New() *Authenticator { - return &Authenticator{sessions: make(map[string]token)} +func New(s *store.Store) *Authenticator { + var out Authenticator + var err error + out.s = s + out.sessions, err = s.GetSessions() + fmt.Println(out.sessions) + if err != nil { + log.Fatal(err) + } + return &out } func (a *Authenticator) login(w http.ResponseWriter, r *http.Request) { @@ -40,18 +53,14 @@ func (a *Authenticator) login(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, wrongPassword) return } - key := generateKey() - a.sessions[key] = token{userName: login, created: time.Now(), lastAccess: time.Now()} - c := http.Cookie{ - Name: "token", - Value: key, - HttpOnly: true, - // #todo (in prod) uncomment this line - //Secure: true, - SameSite: http.SameSiteStrictMode, - Path: "/", + cookie, err := a.newSession(login) + // #todo: change to a some sort of internal server error + if err != nil { + w.Header().Set("content-type", "text/html") + fmt.Fprint(w, "internal server error") + return } - http.SetCookie(w, &c) + http.SetCookie(w, &cookie) http.Redirect(w, r, "/", http.StatusFound) } @@ -67,12 +76,19 @@ func (a *Authenticator) Middleware(in http.Handler) http.Handler { return } value, ok := a.sessions[c.Value] - if !ok || time.Since(value.lastAccess) > time.Hour*24 || time.Since(value.lastAccess) > time.Hour*240 { + if !ok { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + if time.Since(value.Expiration) > 0 { + delete(a.sessions, c.Value) + err := a.s.CleanSessions() + if err != nil { + fmt.Println("error cleaning the sessions", err) + } http.Redirect(w, r, "/login", http.StatusSeeOther) return } - value.lastAccess = time.Now() - a.sessions[c.Value] = value in.ServeHTTP(w, r) }) } diff --git a/internal/auth/login.html b/internal/auth/templates/login.html similarity index 100% rename from internal/auth/login.html rename to internal/auth/templates/login.html diff --git a/internal/auth/wrongPassword.html b/internal/auth/templates/wrongPassword.html similarity index 100% rename from internal/auth/wrongPassword.html rename to internal/auth/templates/wrongPassword.html diff --git a/internal/auth/token.go b/internal/auth/token.go index 5c6ede4..7b3601a 100644 --- a/internal/auth/token.go +++ b/internal/auth/token.go @@ -4,13 +4,31 @@ import ( "crypto/rand" "encoding/base64" "log" + "net/http" "time" + + "github.com/Bronku/iroon/internal/models" ) -type token struct { - userName string - created time.Time - lastAccess time.Time +func (a *Authenticator) newSession(user string) (http.Cookie, error) { + key := generateKey() + var session models.Token + var cookie http.Cookie + session.User = user + session.Expiration = time.Now().Add(time.Hour * 24) + a.sessions[key] = session + err := a.s.AddSession(key, user, session.Expiration) + if err != nil { + return cookie, err + } + + cookie.Name = "token" + cookie.Value = key + cookie.HttpOnly = true + cookie.SameSite = http.SameSiteStrictMode + cookie.Path = "/" + //cookie.Secure = true + return cookie, nil } func generateKey() string { diff --git a/internal/models/auth.go b/internal/models/auth.go new file mode 100644 index 0000000..0e584f1 --- /dev/null +++ b/internal/models/auth.go @@ -0,0 +1,8 @@ +package models + +import "time" + +type Token struct { + User string + Expiration time.Time +} diff --git a/internal/server/server.go b/internal/server/server.go index d4dc392..ae3a1e3 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -14,9 +14,6 @@ type Server struct { } func (h *Server) Close() { - if h.s != nil { - h.s.Close() - } } func (h *Server) loadHandler() { @@ -32,11 +29,11 @@ func (h *Server) loadHandler() { h.Handler = mux } -func New() *Server { +func New(store *store.Store) *Server { var server Server server.loadTemplates() - server.s = store.OpenStore("./foo.db") + server.s = store server.loadHandler() return &server diff --git a/internal/store/auth.go b/internal/store/auth.go new file mode 100644 index 0000000..b34c551 --- /dev/null +++ b/internal/store/auth.go @@ -0,0 +1,52 @@ +package store + +import ( + "time" + + "github.com/Bronku/iroon/internal/models" +) + +func (s *Store) AddSession(token, userName string, expiration time.Time) error { + query := "insert into session (token, user, expiration) values(?, ?, ?)" + _, err := s.db.Exec(query, token, userName, expiration.Format("2006-01-02 15:04")) + return err +} + +func (s *Store) CleanSessions() error { + now := time.Now().Format("2006-01-02 15:04") + query := "delete from session where expiration < ?;" + _, err := s.db.Exec(query, now) + return err +} + +func (s *Store) GetSessions() (map[string]models.Token, error) { + out := make(map[string]models.Token) + err := s.CleanSessions() + if err != nil { + return out, err + } + query := "select token, user, expiration from session;" + rows, err := s.db.Query(query) + if err != nil { + return out, err + } + defer rows.Close() + for rows.Next() { + var current models.Token + var token string + var expiration string + err = rows.Scan(&token, ¤t.User, &expiration) + if err != nil { + continue + } + current.Expiration, err = time.Parse("2006-01-02 15:04", expiration) + if err != nil { + continue + } + if token == "" { + continue + } + out[token] = current + } + return out, nil +} diff --git a/internal/store/migrations/6.sql b/internal/store/migrations/6.sql new file mode 100644 index 0000000..834a963 --- /dev/null +++ b/internal/store/migrations/6.sql @@ -0,0 +1,9 @@ +drop table session; + +create table session ( + token text pirmary key, + user text, + expiration text +); + +pragma user_version = 6; diff --git a/main.go b/main.go index c9e30ae..5dffb9b 100644 --- a/main.go +++ b/main.go @@ -2,19 +2,6 @@ package main import "github.com/Bronku/iroon/cmd/iroon" -type test struct { - data []int -} - -func (t *test) xd() []int { - return t.data -} - -func (t *test) setData(input []int) { - t.data = make([]int, len(input)) - copy(t.data, input) -} - func main() { iroon.Run() }