user_session.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package storage // import "miniflux.app/v2/internal/storage"
  4. import (
  5. "crypto/rand"
  6. "database/sql"
  7. "errors"
  8. "fmt"
  9. "time"
  10. "miniflux.app/v2/internal/model"
  11. )
  12. // UserSessions returns the list of sessions for the given user.
  13. func (s *Storage) UserSessions(userID int64) ([]model.UserSession, error) {
  14. query := `
  15. SELECT
  16. id,
  17. user_id,
  18. token,
  19. created_at,
  20. user_agent,
  21. ip
  22. FROM
  23. user_sessions
  24. WHERE
  25. user_id=$1 ORDER BY id DESC
  26. `
  27. rows, err := s.db.Query(query, userID)
  28. if err != nil {
  29. return nil, fmt.Errorf(`store: unable to fetch user sessions: %v`, err)
  30. }
  31. defer rows.Close()
  32. var sessions []model.UserSession
  33. for rows.Next() {
  34. var session model.UserSession
  35. err := rows.Scan(
  36. &session.ID,
  37. &session.UserID,
  38. &session.Token,
  39. &session.CreatedAt,
  40. &session.UserAgent,
  41. &session.IP,
  42. )
  43. if err != nil {
  44. return nil, fmt.Errorf(`store: unable to fetch user session row: %v`, err)
  45. }
  46. sessions = append(sessions, session)
  47. }
  48. return sessions, nil
  49. }
  50. // CreateUserSessionFromUsername creates a new user session.
  51. func (s *Storage) CreateUserSessionFromUsername(username, userAgent, ip string) (sessionID string, userID int64, err error) {
  52. token := rand.Text()
  53. if ip == "" {
  54. ip = "127.0.0.1"
  55. }
  56. tx, err := s.db.Begin()
  57. if err != nil {
  58. return "", 0, fmt.Errorf(`store: unable to start transaction: %v`, err)
  59. }
  60. err = tx.QueryRow(`SELECT id FROM users WHERE username = LOWER($1)`, username).Scan(&userID)
  61. if err != nil {
  62. tx.Rollback()
  63. return "", 0, fmt.Errorf(`store: unable to fetch user ID: %v`, err)
  64. }
  65. _, err = tx.Exec(
  66. `INSERT INTO user_sessions (token, user_id, user_agent, ip) VALUES ($1, $2, $3, $4)`,
  67. token,
  68. userID,
  69. userAgent,
  70. ip,
  71. )
  72. if err != nil {
  73. tx.Rollback()
  74. return "", 0, fmt.Errorf(`store: unable to create user session: %v`, err)
  75. }
  76. if err := tx.Commit(); err != nil {
  77. return "", 0, fmt.Errorf(`store: unable to commit transaction: %v`, err)
  78. }
  79. return token, userID, nil
  80. }
  81. // UserSessionByToken finds a session by the token.
  82. func (s *Storage) UserSessionByToken(token string) (*model.UserSession, error) {
  83. var session model.UserSession
  84. query := `
  85. SELECT
  86. id,
  87. user_id,
  88. token,
  89. created_at,
  90. user_agent,
  91. ip
  92. FROM
  93. user_sessions
  94. WHERE
  95. token = $1
  96. `
  97. err := s.db.QueryRow(query, token).Scan(
  98. &session.ID,
  99. &session.UserID,
  100. &session.Token,
  101. &session.CreatedAt,
  102. &session.UserAgent,
  103. &session.IP,
  104. )
  105. switch {
  106. case err == sql.ErrNoRows:
  107. return nil, nil
  108. case err != nil:
  109. return nil, fmt.Errorf(`store: unable to fetch user session: %v`, err)
  110. default:
  111. return &session, nil
  112. }
  113. }
  114. // RemoveUserSessionByToken remove a session by using the token.
  115. func (s *Storage) RemoveUserSessionByToken(userID int64, token string) error {
  116. query := `DELETE FROM user_sessions WHERE user_id=$1 AND token=$2`
  117. result, err := s.db.Exec(query, userID, token)
  118. if err != nil {
  119. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  120. }
  121. count, err := result.RowsAffected()
  122. if err != nil {
  123. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  124. }
  125. if count != 1 {
  126. return errors.New(`store: nothing has been removed`)
  127. }
  128. return nil
  129. }
  130. // RemoveUserSessionByID remove a session by using the ID.
  131. func (s *Storage) RemoveUserSessionByID(userID, sessionID int64) error {
  132. query := `DELETE FROM user_sessions WHERE user_id=$1 AND id=$2`
  133. result, err := s.db.Exec(query, userID, sessionID)
  134. if err != nil {
  135. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  136. }
  137. count, err := result.RowsAffected()
  138. if err != nil {
  139. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  140. }
  141. if count != 1 {
  142. return errors.New(`store: nothing has been removed`)
  143. }
  144. return nil
  145. }
  146. // CleanOldUserSessions removes user sessions older than specified interval (24h minimum).
  147. func (s *Storage) CleanOldUserSessions(interval time.Duration) int64 {
  148. query := `
  149. DELETE FROM
  150. user_sessions
  151. WHERE
  152. created_at < now() - $1::interval
  153. `
  154. days := max(int(interval/(24*time.Hour)), 1)
  155. result, err := s.db.Exec(query, fmt.Sprintf("%d days", days))
  156. if err != nil {
  157. return 0
  158. }
  159. n, _ := result.RowsAffected()
  160. return n
  161. }