middleware.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package googlereader // import "miniflux.app/v2/internal/googlereader"
  4. import (
  5. "context"
  6. "crypto/hmac"
  7. "crypto/sha1"
  8. "encoding/hex"
  9. "log/slog"
  10. "net/http"
  11. "strings"
  12. "miniflux.app/v2/internal/http/request"
  13. "miniflux.app/v2/internal/model"
  14. "miniflux.app/v2/internal/storage"
  15. )
  16. type middleware struct {
  17. store *storage.Storage
  18. }
  19. func newMiddleware(s *storage.Storage) *middleware {
  20. return &middleware{s}
  21. }
  22. func (m *middleware) handleCORS(next http.Handler) http.Handler {
  23. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  24. w.Header().Set("Access-Control-Allow-Origin", "*")
  25. w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
  26. w.Header().Set("Access-Control-Allow-Headers", "Authorization")
  27. if r.Method == http.MethodOptions {
  28. w.WriteHeader(http.StatusOK)
  29. return
  30. }
  31. next.ServeHTTP(w, r)
  32. })
  33. }
  34. func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
  35. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  36. clientIP := request.ClientIP(r)
  37. var token string
  38. if r.Method == http.MethodPost {
  39. if err := r.ParseForm(); err != nil {
  40. slog.Warn("[GoogleReader] Could not parse request form data",
  41. slog.Bool("authentication_failed", true),
  42. slog.String("client_ip", clientIP),
  43. slog.String("user_agent", r.UserAgent()),
  44. slog.Any("error", err),
  45. )
  46. sendUnauthorizedResponse(w)
  47. return
  48. }
  49. token = r.Form.Get("T")
  50. if token == "" {
  51. slog.Warn("[GoogleReader] Post-Form T field is empty",
  52. slog.Bool("authentication_failed", true),
  53. slog.String("client_ip", clientIP),
  54. slog.String("user_agent", r.UserAgent()),
  55. )
  56. sendUnauthorizedResponse(w)
  57. return
  58. }
  59. } else {
  60. authorization := r.Header.Get("Authorization")
  61. if authorization == "" {
  62. slog.Warn("[GoogleReader] No token provided",
  63. slog.Bool("authentication_failed", true),
  64. slog.String("client_ip", clientIP),
  65. slog.String("user_agent", r.UserAgent()),
  66. )
  67. sendUnauthorizedResponse(w)
  68. return
  69. }
  70. fields := strings.Fields(authorization)
  71. if len(fields) != 2 {
  72. slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
  73. slog.Bool("authentication_failed", true),
  74. slog.String("client_ip", clientIP),
  75. slog.String("user_agent", r.UserAgent()),
  76. )
  77. sendUnauthorizedResponse(w)
  78. return
  79. }
  80. if fields[0] != "GoogleLogin" {
  81. slog.Warn("[GoogleReader] Authorization header does not begin with GoogleLogin",
  82. slog.Bool("authentication_failed", true),
  83. slog.String("client_ip", clientIP),
  84. slog.String("user_agent", r.UserAgent()),
  85. )
  86. sendUnauthorizedResponse(w)
  87. return
  88. }
  89. auths := strings.Split(fields[1], "=")
  90. if len(auths) != 2 {
  91. slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
  92. slog.Bool("authentication_failed", true),
  93. slog.String("client_ip", clientIP),
  94. slog.String("user_agent", r.UserAgent()),
  95. )
  96. sendUnauthorizedResponse(w)
  97. return
  98. }
  99. if auths[0] != "auth" {
  100. slog.Warn("[GoogleReader] Authorization header does not have the expected GoogleLogin format auth=xxxxxx",
  101. slog.Bool("authentication_failed", true),
  102. slog.String("client_ip", clientIP),
  103. slog.String("user_agent", r.UserAgent()),
  104. )
  105. sendUnauthorizedResponse(w)
  106. return
  107. }
  108. token = auths[1]
  109. }
  110. parts := strings.Split(token, "/")
  111. if len(parts) != 2 {
  112. slog.Warn("[GoogleReader] Auth token does not have the expected structure username/hash",
  113. slog.Bool("authentication_failed", true),
  114. slog.String("client_ip", clientIP),
  115. slog.String("user_agent", r.UserAgent()),
  116. slog.String("token", token),
  117. )
  118. sendUnauthorizedResponse(w)
  119. return
  120. }
  121. var integration *model.Integration
  122. var user *model.User
  123. var err error
  124. if integration, err = m.store.GoogleReaderUserGetIntegration(parts[0]); err != nil {
  125. slog.Warn("[GoogleReader] No user found with the given Google Reader username",
  126. slog.Bool("authentication_failed", true),
  127. slog.String("client_ip", clientIP),
  128. slog.String("user_agent", r.UserAgent()),
  129. slog.Any("error", err),
  130. )
  131. sendUnauthorizedResponse(w)
  132. return
  133. }
  134. expectedToken := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
  135. if expectedToken != token {
  136. slog.Warn("[GoogleReader] Token does not match",
  137. slog.Bool("authentication_failed", true),
  138. slog.String("client_ip", clientIP),
  139. slog.String("user_agent", r.UserAgent()),
  140. )
  141. sendUnauthorizedResponse(w)
  142. return
  143. }
  144. if user, err = m.store.UserByID(integration.UserID); err != nil {
  145. slog.Error("[GoogleReader] Unable to fetch user from database",
  146. slog.Bool("authentication_failed", true),
  147. slog.String("client_ip", clientIP),
  148. slog.String("user_agent", r.UserAgent()),
  149. slog.Any("error", err),
  150. )
  151. sendUnauthorizedResponse(w)
  152. return
  153. }
  154. if user == nil {
  155. slog.Warn("[GoogleReader] No user found with the given Google Reader credentials",
  156. slog.Bool("authentication_failed", true),
  157. slog.String("client_ip", clientIP),
  158. slog.String("user_agent", r.UserAgent()),
  159. )
  160. sendUnauthorizedResponse(w)
  161. return
  162. }
  163. m.store.SetLastLogin(integration.UserID)
  164. ctx := r.Context()
  165. ctx = context.WithValue(ctx, request.UserIDContextKey, user.ID)
  166. ctx = context.WithValue(ctx, request.UserNameContextKey, user.Username)
  167. ctx = context.WithValue(ctx, request.UserTimezoneContextKey, user.Timezone)
  168. ctx = context.WithValue(ctx, request.IsAdminUserContextKey, user.IsAdmin)
  169. ctx = context.WithValue(ctx, request.IsAuthenticatedContextKey, true)
  170. ctx = context.WithValue(ctx, request.GoogleReaderTokenKey, token)
  171. next.ServeHTTP(w, r.WithContext(ctx))
  172. })
  173. }
  174. func getAuthToken(username, password string) string {
  175. token := hex.EncodeToString(hmac.New(sha1.New, []byte(username+password)).Sum(nil))
  176. token = username + "/" + token
  177. return token
  178. }