user_session.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. tx, err := s.db.Begin()
  54. if err != nil {
  55. return "", 0, fmt.Errorf(`store: unable to start transaction: %v`, err)
  56. }
  57. err = tx.QueryRow(`SELECT id FROM users WHERE username = LOWER($1)`, username).Scan(&userID)
  58. if err != nil {
  59. tx.Rollback()
  60. return "", 0, fmt.Errorf(`store: unable to fetch user ID: %v`, err)
  61. }
  62. _, err = tx.Exec(
  63. `INSERT INTO user_sessions (token, user_id, user_agent, ip) VALUES ($1, $2, $3, $4)`,
  64. token,
  65. userID,
  66. userAgent,
  67. ip,
  68. )
  69. if err != nil {
  70. tx.Rollback()
  71. return "", 0, fmt.Errorf(`store: unable to create user session: %v`, err)
  72. }
  73. if err := tx.Commit(); err != nil {
  74. return "", 0, fmt.Errorf(`store: unable to commit transaction: %v`, err)
  75. }
  76. return token, userID, nil
  77. }
  78. // UserSessionByToken finds a session by the token.
  79. func (s *Storage) UserSessionByToken(token string) (*model.UserSession, error) {
  80. var session model.UserSession
  81. query := `
  82. SELECT
  83. id,
  84. user_id,
  85. token,
  86. created_at,
  87. user_agent,
  88. ip
  89. FROM
  90. user_sessions
  91. WHERE
  92. token = $1
  93. `
  94. err := s.db.QueryRow(query, token).Scan(
  95. &session.ID,
  96. &session.UserID,
  97. &session.Token,
  98. &session.CreatedAt,
  99. &session.UserAgent,
  100. &session.IP,
  101. )
  102. switch {
  103. case err == sql.ErrNoRows:
  104. return nil, nil
  105. case err != nil:
  106. return nil, fmt.Errorf(`store: unable to fetch user session: %v`, err)
  107. default:
  108. return &session, nil
  109. }
  110. }
  111. // RemoveUserSessionByToken remove a session by using the token.
  112. func (s *Storage) RemoveUserSessionByToken(userID int64, token string) error {
  113. query := `DELETE FROM user_sessions WHERE user_id=$1 AND token=$2`
  114. result, err := s.db.Exec(query, userID, token)
  115. if err != nil {
  116. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  117. }
  118. count, err := result.RowsAffected()
  119. if err != nil {
  120. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  121. }
  122. if count != 1 {
  123. return errors.New(`store: nothing has been removed`)
  124. }
  125. return nil
  126. }
  127. // RemoveUserSessionByID remove a session by using the ID.
  128. func (s *Storage) RemoveUserSessionByID(userID, sessionID int64) error {
  129. query := `DELETE FROM user_sessions WHERE user_id=$1 AND id=$2`
  130. result, err := s.db.Exec(query, userID, sessionID)
  131. if err != nil {
  132. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  133. }
  134. count, err := result.RowsAffected()
  135. if err != nil {
  136. return fmt.Errorf(`store: unable to remove this user session: %v`, err)
  137. }
  138. if count != 1 {
  139. return errors.New(`store: nothing has been removed`)
  140. }
  141. return nil
  142. }
  143. // CleanOldUserSessions removes user sessions older than specified interval (24h minimum).
  144. func (s *Storage) CleanOldUserSessions(interval time.Duration) int64 {
  145. query := `
  146. DELETE FROM
  147. user_sessions
  148. WHERE
  149. created_at < now() - $1::interval
  150. `
  151. days := max(int(interval/(24*time.Hour)), 1)
  152. result, err := s.db.Exec(query, fmt.Sprintf("%d days", days))
  153. if err != nil {
  154. return 0
  155. }
  156. n, _ := result.RowsAffected()
  157. return n
  158. }