From 7fabadc871ea91ea22fe9454e2ca4b33e5c211be Mon Sep 17 00:00:00 2001 From: Dardan Date: Thu, 2 Nov 2023 22:01:56 +0100 Subject: [PATCH] feat: make user session timeout configurable (#2753) Co-authored-by: Oleg Lobanov --- http/auth.go | 42 +++++++++++++++++++++++------------------- http/http.go | 5 +++-- settings/settings.go | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/http/auth.go b/http/auth.go index 75c66149..69bc7dfb 100644 --- a/http/auth.go +++ b/http/auth.go @@ -16,7 +16,7 @@ import ( ) const ( - TokenExpirationTime = time.Hour * 2 + DefaultTokenExpirationTime = time.Hour * 2 ) type userInfo struct { @@ -101,19 +101,21 @@ func withAdmin(fn handleFunc) handleFunc { }) } -var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - auther, err := d.store.Auth.Get(d.settings.AuthMethod) - if err != nil { - return http.StatusInternalServerError, err - } +func loginHandler(tokenExpireTime time.Duration) handleFunc { + return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + auther, err := d.store.Auth.Get(d.settings.AuthMethod) + if err != nil { + return http.StatusInternalServerError, err + } - user, err := auther.Auth(r, d.store.Users, d.settings, d.server) - if err == os.ErrPermission { - return http.StatusForbidden, nil - } else if err != nil { - return http.StatusInternalServerError, err - } else { - return printToken(w, r, d, user) + user, err := auther.Auth(r, d.store.Users, d.settings, d.server) + if err == os.ErrPermission { + return http.StatusForbidden, nil + } else if err != nil { + return http.StatusInternalServerError, err + } else { + return printToken(w, r, d, user, tokenExpireTime) + } } } @@ -172,12 +174,14 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, return http.StatusOK, nil } -var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - w.Header().Set("X-Renew-Token", "false") - return printToken(w, r, d, d.user) -}) +func renewHandler(tokenExpireTime time.Duration) handleFunc { + return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + w.Header().Set("X-Renew-Token", "false") + return printToken(w, r, d, d.user, tokenExpireTime) + }) +} -func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) { +func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) { claims := &authToken{ User: userInfo{ ID: user.ID, @@ -192,7 +196,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use }, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), - ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(tokenExpirationTime)), Issuer: "File Browser", }, } diff --git a/http/http.go b/http/http.go index 984d1d1c..8d0c4af7 100644 --- a/http/http.go +++ b/http/http.go @@ -48,9 +48,10 @@ func NewHandler( api := r.PathPrefix("/api").Subrouter() - api.Handle("/login", monkey(loginHandler, "")) + tokenExpirationTime := server.GetTokenExpirationTime(DefaultTokenExpirationTime) + api.Handle("/login", monkey(loginHandler(tokenExpirationTime), "")) api.Handle("/signup", monkey(signupHandler, "")) - api.Handle("/renew", monkey(renewHandler, "")) + api.Handle("/renew", monkey(renewHandler(tokenExpirationTime), "")) users := api.PathPrefix("/users").Subrouter() users.Handle("", monkey(usersGetHandler, "")).Methods("GET") diff --git a/settings/settings.go b/settings/settings.go index 35ba1278..4e62159b 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -2,7 +2,9 @@ package settings import ( "crypto/rand" + "log" "strings" + "time" "github.com/filebrowser/filebrowser/v2/rules" ) @@ -47,6 +49,7 @@ type Server struct { EnableExec bool `json:"enableExec"` TypeDetectionByHeader bool `json:"typeDetectionByHeader"` AuthHook string `json:"authHook"` + TokenExpirationTime string `json:"tokenExpirationTime"` } // Clean cleans any variables that might need cleaning. @@ -54,6 +57,19 @@ func (s *Server) Clean() { s.BaseURL = strings.TrimSuffix(s.BaseURL, "/") } +func (s *Server) GetTokenExpirationTime(fallback time.Duration) time.Duration { + if s.TokenExpirationTime == "" { + return fallback + } + + if duration, err := time.ParseDuration(s.TokenExpirationTime); err == nil { + return duration + } else { + log.Printf("[WARN] Failed to parse tokenExpirationTime: %v", err) + return fallback + } +} + // GenerateKey generates a key of 512 bits. func GenerateKey() ([]byte, error) { b := make([]byte, 64) //nolint:gomnd