| @@ -80,15 +80,18 @@ func (s *Store) CreateWebSession(hash string, userID int64, ttl time.Duration) e |
| 80 | return err |
80 | return err |
| 81 | } |
81 | } |
| 82 | |
82 | |
| 83 | // WebSessionUser resolves a session cookie hash to its user and renews |
83 | // WebSessionUser resolves a session cookie hash to its user, with the |
| 84 | // the session's idle expiry. A session is written at most once a |
84 | // session's sign-in time, and renews the session's idle expiry. A |
| 85 | // minute, so a burst of requests costs one UPDATE. |
85 | // session is written at most once a minute, so a burst of requests |
| |
86 | // costs one UPDATE. Renewal never moves created_at: only a login |
| |
87 | // creates a session, so created_at is when it signed in. |
| 86 | func (s *Store) WebSessionUser(hash string) (User, error) { |
88 | func (s *Store) WebSessionUser(hash string) (User, error) { |
| 87 | now := time.Now() |
89 | now := time.Now() |
| 88 | var userID int64 |
90 | var userID int64 |
| |
91 | var created string |
| 89 | err := s.DB.QueryRow( |
92 | err := s.DB.QueryRow( |
| 90 | "SELECT user_id FROM web_sessions WHERE token_hash = ? AND expires_at > ?", |
93 | "SELECT user_id, created_at FROM web_sessions WHERE token_hash = ? AND expires_at > ?", |
| 91 | hash, fmtTime(now)).Scan(&userID) |
94 | hash, fmtTime(now)).Scan(&userID, &created) |
| 92 | if errors.Is(err, sql.ErrNoRows) { |
95 | if errors.Is(err, sql.ErrNoRows) { |
| 93 | return User{}, ErrNotFound |
96 | return User{}, ErrNotFound |
| 94 | } |
97 | } |
| @@ -98,7 +101,14 @@ func (s *Store) WebSessionUser(hash string) (User, error) { |
| 98 | s.DB.Exec(`UPDATE web_sessions SET last_used_at = ?, expires_at = min(absolute_expires_at, ?) |
101 | s.DB.Exec(`UPDATE web_sessions SET last_used_at = ?, expires_at = min(absolute_expires_at, ?) |
| 99 | WHERE token_hash = ? AND last_used_at < ?`, |
102 | WHERE token_hash = ? AND last_used_at < ?`, |
| 100 | fmtTime(now), fmtTime(now.Add(WebSessionIdle)), hash, fmtTime(now.Add(-time.Minute))) |
103 | fmtTime(now), fmtTime(now.Add(WebSessionIdle)), hash, fmtTime(now.Add(-time.Minute))) |
| 101 | return s.UserByID(userID) |
104 | u, err := s.UserByID(userID) |
| |
105 | if err != nil { |
| |
106 | return User{}, err |
| |
107 | } |
| |
108 | if t := parseTime(sql.NullString{String: created, Valid: true}); t != nil { |
| |
109 | u.SignedInAt = *t |
| |
110 | } |
| |
111 | return u, nil |
| 102 | } |
112 | } |
| 103 | |
113 | |
| 104 | func (s *Store) DeleteWebSession(hash string) error { |
114 | func (s *Store) DeleteWebSession(hash string) error { |