github.com/decred/politeia@v1.4.0/politeiawww/legacy/sessions/sessions.go (about) 1 // Copyright (c) 2020-2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package sessions 6 7 import ( 8 "errors" 9 "net/http" 10 "time" 11 12 www "github.com/decred/politeia/politeiawww/api/www/v1" 13 "github.com/decred/politeia/politeiawww/legacy/user" 14 "github.com/google/uuid" 15 "github.com/gorilla/sessions" 16 ) 17 18 const ( 19 // SessionMaxAge is the max age for a session in seconds. 20 SessionMaxAge = 86400 // One day 21 22 // Session value keys. A user session contains a map that is used 23 // for application specific values. The following is a list of the 24 // keys for the politeiawww specific values. 25 sessionValueUserID = "user_id" 26 sessionValueCreatedAt = "created_at" 27 ) 28 29 var ( 30 // ErrSessionNotFound is emitted when a session is not found in the 31 // session store. 32 ErrSessionNotFound = errors.New("session not found") 33 ) 34 35 // Sessions manages politeiawww sessions. 36 type Sessions struct { 37 store sessions.Store 38 userdb user.Database 39 } 40 41 func sessionIsExpired(session *sessions.Session) bool { 42 createdAt := session.Values[sessionValueCreatedAt].(int64) 43 expiresAt := createdAt + int64(session.Options.MaxAge) 44 return time.Now().Unix() > expiresAt 45 } 46 47 // GetSession returns the Session for the session ID from the given http 48 // request cookie. If no session exists then a new session object is returned. 49 // Access IsNew on the session to check if it is an existing session or a new 50 // one. The new session will not have any sessions values set, such as user_id, 51 // and will not have been saved to the session store yet. 52 func (s *Sessions) GetSession(r *http.Request) (*sessions.Session, error) { 53 log.Tracef("GetSession") 54 55 return s.store.Get(r, www.CookieSession) 56 } 57 58 // GetSessionUserID returns the user ID of the user for the given session. A 59 // ErrSessionNotFound error is returned if a user session does not exist or 60 // has expired. 61 func (s *Sessions) GetSessionUserID(w http.ResponseWriter, r *http.Request) (string, error) { 62 log.Tracef("GetSessionUserID") 63 64 session, err := s.GetSession(r) 65 if err != nil { 66 return "", err 67 } 68 if session.IsNew { 69 // If the session is new it means the request did not contain a 70 // valid session. This could be because it was expired or it 71 // did not exist. 72 log.Debugf("Session not found for user") 73 return "", ErrSessionNotFound 74 } 75 76 // Delete the session if its expired. Setting the MaxAge to <= 0 77 // and saving the session will trigger a deletion. The previous 78 // GetSession call should already filter out expired sessions so 79 // this is really just a sanity check. 80 if sessionIsExpired(session) { 81 log.Debug("Session is expired") 82 session.Options.MaxAge = -1 83 s.store.Save(r, w, session) 84 return "", ErrSessionNotFound 85 } 86 87 return session.Values[sessionValueUserID].(string), nil 88 } 89 90 // GetSessionUser returns the User for the given session. A errSessionFound 91 // error is returned if a user session does not exist or has expired. 92 func (s *Sessions) GetSessionUser(w http.ResponseWriter, r *http.Request) (*user.User, error) { 93 log.Tracef("GetSessionUser") 94 95 uid, err := s.GetSessionUserID(w, r) 96 if err != nil { 97 return nil, err 98 } 99 100 pid, err := uuid.Parse(uid) 101 if err != nil { 102 return nil, err 103 } 104 105 user, err := s.userdb.UserGetById(pid) 106 if err != nil { 107 return nil, err 108 } 109 110 if user.Deactivated { 111 log.Debugf("User has been deactivated") 112 err := s.DelSession(w, r) 113 if err != nil { 114 return nil, err 115 } 116 return nil, ErrSessionNotFound 117 } 118 119 log.Debugf("Session found for user %v", user.ID) 120 121 return user, nil 122 } 123 124 // DelSession removes the given session from the session store. 125 func (s *Sessions) DelSession(w http.ResponseWriter, r *http.Request) error { 126 log.Tracef("DelSession") 127 128 session, err := s.GetSession(r) 129 if err != nil { 130 return err 131 } 132 if session.IsNew { 133 return ErrSessionNotFound 134 } 135 136 log.Debugf("Deleting user session %v", session.Values[sessionValueUserID]) 137 138 // Saving the session with a negative MaxAge will cause it to be 139 // deleted. 140 session.Options.MaxAge = -1 141 return s.store.Save(r, w, session) 142 } 143 144 // NewSession creates a new session, adds it to the given http response 145 // session cookie, and saves it to the session store. If the http request 146 // already contains a session cookie then the session values will be updated 147 // and the session will be updated in the session store. 148 func (s *Sessions) NewSession(w http.ResponseWriter, r *http.Request, userID string) error { 149 log.Tracef("NewSession: %v", userID) 150 151 // Init session 152 session, err := s.GetSession(r) 153 if err != nil { 154 return err 155 } 156 157 // Update session with politeiawww specific values 158 session.Values[sessionValueCreatedAt] = time.Now().Unix() 159 session.Values[sessionValueUserID] = userID 160 161 log.Debugf("Session created for user %v", userID) 162 163 // Update session in the store and update the response cookie 164 return s.store.Save(r, w, session) 165 } 166 167 // New returns a new Sessions context. 168 func New(userdb user.Database, keyPairs ...[]byte) *Sessions { 169 return &Sessions{ 170 store: newSessionStore(userdb, keyPairs...), 171 userdb: userdb, 172 } 173 }