From 7897d8a8ad87c0bb26bb8156ba09fc9fab62f565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Mon, 21 Jan 2019 20:21:05 -0800 Subject: [PATCH] Handle the case when application session is expired and not user session --- model/app_session.go | 2 +- storage/session.go | 54 +++++++++++++++++++++++++++++++------------ ui/middleware.go | 23 ++++++++++++------ ui/session/session.go | 16 ++++++------- ui/ui.go | 2 +- 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/model/app_session.go b/model/app_session.go index 6a1f6ff7..ad4db3bb 100644 --- a/model/app_session.go +++ b/model/app_session.go @@ -18,7 +18,7 @@ type SessionData struct { FlashMessage string `json:"flash_message"` FlashErrorMessage string `json:"flash_error_message"` Language string `json:"language"` - Theme string `json:"Theme"` + Theme string `json:"theme"` PocketRequestToken string `json:"pocket_request_token"` } diff --git a/storage/session.go b/storage/session.go index 41396848..8c1f615a 100644 --- a/storage/session.go +++ b/storage/session.go @@ -12,24 +12,48 @@ import ( "miniflux.app/model" ) -// CreateSession creates a new session. -func (s *Storage) CreateSession() (*model.Session, error) { - session := model.Session{ - ID: crypto.GenerateRandomString(32), - Data: &model.SessionData{CSRF: crypto.GenerateRandomString(64)}, - } - - query := "INSERT INTO sessions (id, data) VALUES ($1, $2)" - _, err := s.db.Exec(query, session.ID, session.Data) +// CreateAppSessionWithUserPrefs creates a new application session with the given user preferences. +func (s *Storage) CreateAppSessionWithUserPrefs(userID int64) (*model.Session, error) { + user, err := s.UserByID(userID) if err != nil { - return nil, fmt.Errorf("unable to create session: %v", err) + return nil, err } - return &session, nil + session := model.Session{ + ID: crypto.GenerateRandomString(32), + Data: &model.SessionData{ + CSRF: crypto.GenerateRandomString(64), + Theme: user.Theme, + Language: user.Language, + }, + } + + return s.createAppSession(&session) } -// UpdateSessionField updates only one session field. -func (s *Storage) UpdateSessionField(sessionID, field string, value interface{}) error { +// CreateAppSession creates a new application session. +func (s *Storage) CreateAppSession() (*model.Session, error) { + session := model.Session{ + ID: crypto.GenerateRandomString(32), + Data: &model.SessionData{ + CSRF: crypto.GenerateRandomString(64), + }, + } + + return s.createAppSession(&session) +} + +func (s *Storage) createAppSession(session *model.Session) (*model.Session, error) { + _, err := s.db.Exec(`INSERT INTO sessions (id, data) VALUES ($1, $2)`, session.ID, session.Data) + if err != nil { + return nil, fmt.Errorf("unable to create app session: %v", err) + } + + return session, nil +} + +// UpdateAppSessionField updates only one session field. +func (s *Storage) UpdateAppSessionField(sessionID, field string, value interface{}) error { query := `UPDATE sessions SET data = jsonb_set(data, '{%s}', to_jsonb($1::text), true) WHERE id=$2` @@ -42,8 +66,8 @@ func (s *Storage) UpdateSessionField(sessionID, field string, value interface{}) return nil } -// Session returns the given session. -func (s *Storage) Session(id string) (*model.Session, error) { +// AppSession returns the given session. +func (s *Storage) AppSession(id string) (*model.Session, error) { var session model.Session query := "SELECT id, data FROM sessions WHERE id=$1" diff --git a/ui/middleware.go b/ui/middleware.go index 237432a1..81fb3a52 100644 --- a/ui/middleware.go +++ b/ui/middleware.go @@ -61,12 +61,21 @@ func (m *middleware) handleAppSession(next http.Handler) http.Handler { session := m.getAppSessionValueFromCookie(r) if session == nil { - logger.Debug("[UI:AppSession] Session not found") - - session, err = m.store.CreateSession() - if err != nil { - html.ServerError(w, r, err) - return + if (request.IsAuthenticated(r)) { + userID := request.UserID(r) + logger.Debug("[UI:AppSession] Cookie expired but user #%d is logged: creating a new session", userID) + session, err = m.store.CreateAppSessionWithUserPrefs(userID) + if err != nil { + html.ServerError(w, r, err) + return + } + } else { + logger.Debug("[UI:AppSession] Session not found, creating a new one") + session, err = m.store.CreateAppSession() + if err != nil { + html.ServerError(w, r, err) + return + } } http.SetCookie(w, cookie.New(cookie.CookieSessionID, session.ID, m.cfg.IsHTTPS, m.cfg.BasePath())) @@ -104,7 +113,7 @@ func (m *middleware) getAppSessionValueFromCookie(r *http.Request) *model.Sessio return nil } - session, err := m.store.Session(cookieValue) + session, err := m.store.AppSession(cookieValue) if err != nil { logger.Error("[UI:AppSession] %v", err) return nil diff --git a/ui/session/session.go b/ui/session/session.go index 23518664..0874f927 100644 --- a/ui/session/session.go +++ b/ui/session/session.go @@ -18,49 +18,49 @@ type Session struct { // NewOAuth2State generates a new OAuth2 state and stores the value into the database. func (s *Session) NewOAuth2State() string { state := crypto.GenerateRandomString(32) - s.store.UpdateSessionField(s.sessionID, "oauth2_state", state) + s.store.UpdateAppSessionField(s.sessionID, "oauth2_state", state) return state } // NewFlashMessage creates a new flash message. func (s *Session) NewFlashMessage(message string) { - s.store.UpdateSessionField(s.sessionID, "flash_message", message) + s.store.UpdateAppSessionField(s.sessionID, "flash_message", message) } // FlashMessage returns the current flash message if any. func (s *Session) FlashMessage(message string) string { if message != "" { - s.store.UpdateSessionField(s.sessionID, "flash_message", "") + s.store.UpdateAppSessionField(s.sessionID, "flash_message", "") } return message } // NewFlashErrorMessage creates a new flash error message. func (s *Session) NewFlashErrorMessage(message string) { - s.store.UpdateSessionField(s.sessionID, "flash_error_message", message) + s.store.UpdateAppSessionField(s.sessionID, "flash_error_message", message) } // FlashErrorMessage returns the last flash error message if any. func (s *Session) FlashErrorMessage(message string) string { if message != "" { - s.store.UpdateSessionField(s.sessionID, "flash_error_message", "") + s.store.UpdateAppSessionField(s.sessionID, "flash_error_message", "") } return message } // SetLanguage updates the language field in session. func (s *Session) SetLanguage(language string) { - s.store.UpdateSessionField(s.sessionID, "language", language) + s.store.UpdateAppSessionField(s.sessionID, "language", language) } // SetTheme updates the theme field in session. func (s *Session) SetTheme(theme string) { - s.store.UpdateSessionField(s.sessionID, "theme", theme) + s.store.UpdateAppSessionField(s.sessionID, "theme", theme) } // SetPocketRequestToken updates Pocket Request Token. func (s *Session) SetPocketRequestToken(requestToken string) { - s.store.UpdateSessionField(s.sessionID, "pocket_request_token", requestToken) + s.store.UpdateAppSessionField(s.sessionID, "pocket_request_token", requestToken) } // New returns a new session handler. diff --git a/ui/ui.go b/ui/ui.go index 91d47839..71bbe699 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -22,8 +22,8 @@ func Serve(router *mux.Router, cfg *config.Config, store *storage.Storage, pool handler := &handler{router, cfg, store, template.NewEngine(cfg, router), pool, feedHandler} uiRouter := router.NewRoute().Subrouter() - uiRouter.Use(middleware.handleAppSession) uiRouter.Use(middleware.handleUserSession) + uiRouter.Use(middleware.handleAppSession) // Static assets. uiRouter.HandleFunc("/stylesheets/{name}.css", handler.showStylesheet).Name("stylesheet").Methods("GET")