web_session_middleware.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package ui // import "miniflux.app/v2/internal/ui"
  4. import (
  5. "context"
  6. "log/slog"
  7. "net/http"
  8. "strings"
  9. "miniflux.app/v2/internal/http/request"
  10. "miniflux.app/v2/internal/http/response"
  11. "miniflux.app/v2/internal/model"
  12. "miniflux.app/v2/internal/storage"
  13. )
  14. type webSessionMiddleware struct {
  15. basePath string
  16. store *storage.Storage
  17. }
  18. func newWebSessionMiddleware(basePath string, store *storage.Storage) *webSessionMiddleware {
  19. return &webSessionMiddleware{basePath: basePath, store: store}
  20. }
  21. func (m *webSessionMiddleware) handle(next http.Handler) http.Handler {
  22. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  23. if isStaticAssetRoute(r) {
  24. next.ServeHTTP(w, r)
  25. return
  26. }
  27. session, err := m.loadWebSessionFromCookie(r)
  28. if err != nil {
  29. response.HTMLServerError(w, r, err)
  30. return
  31. }
  32. if session == nil {
  33. var secret string
  34. session, secret = model.NewWebSession(r.UserAgent(), request.ClientIP(r))
  35. if err := m.store.CreateWebSession(session); err != nil {
  36. response.HTMLServerError(w, r, err)
  37. return
  38. }
  39. setSessionCookie(w, session, secret)
  40. }
  41. ctx := context.WithValue(r.Context(), request.WebSessionContextKey, session)
  42. r = r.WithContext(ctx)
  43. if !request.IsAuthenticated(r) && !isPublicRoute(r) {
  44. response.HTMLRedirect(w, r, loginRedirectURL(m.basePath, r.RequestURI))
  45. return
  46. }
  47. next.ServeHTTP(w, r)
  48. if session.IsDirty() {
  49. if err := m.store.UpdateWebSession(session); err != nil {
  50. slog.Error("Unable to persist web session changes",
  51. slog.String("session_id", session.ID),
  52. slog.Any("error", err),
  53. )
  54. }
  55. }
  56. })
  57. }
  58. func (m *webSessionMiddleware) loadWebSessionFromCookie(r *http.Request) (*model.WebSession, error) {
  59. cookieValue := request.CookieValue(r, sessionCookieName)
  60. if cookieValue == "" {
  61. return nil, nil
  62. }
  63. sessionID, secret, ok := strings.Cut(cookieValue, ".")
  64. if !ok || sessionID == "" || secret == "" {
  65. return nil, nil
  66. }
  67. session, err := m.store.WebSessionByID(sessionID)
  68. if err != nil {
  69. return nil, err
  70. }
  71. if session == nil || !session.VerifySecret(secret) {
  72. return nil, nil
  73. }
  74. return session, nil
  75. }