github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/store/sqlstore/session_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 sq "github.com/Masterminds/squirrel" 12 "github.com/pkg/errors" 13 14 "github.com/mattermost/mattermost-server/v5/mlog" 15 "github.com/mattermost/mattermost-server/v5/model" 16 "github.com/mattermost/mattermost-server/v5/store" 17 ) 18 19 const ( 20 SessionsCleanupDelayMilliseconds = 100 21 ) 22 23 type SqlSessionStore struct { 24 *SqlStore 25 } 26 27 func newSqlSessionStore(sqlStore *SqlStore) store.SessionStore { 28 us := &SqlSessionStore{sqlStore} 29 30 for _, db := range sqlStore.GetAllConns() { 31 table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id") 32 table.ColMap("Id").SetMaxSize(26) 33 table.ColMap("Token").SetMaxSize(26) 34 table.ColMap("UserId").SetMaxSize(26) 35 table.ColMap("DeviceId").SetMaxSize(512) 36 table.ColMap("Roles").SetMaxSize(64) 37 table.ColMap("Props").SetMaxSize(1000) 38 } 39 40 return us 41 } 42 43 func (me SqlSessionStore) createIndexesIfNotExists() { 44 me.CreateIndexIfNotExists("idx_sessions_user_id", "Sessions", "UserId") 45 me.CreateIndexIfNotExists("idx_sessions_token", "Sessions", "Token") 46 me.CreateIndexIfNotExists("idx_sessions_expires_at", "Sessions", "ExpiresAt") 47 me.CreateIndexIfNotExists("idx_sessions_create_at", "Sessions", "CreateAt") 48 me.CreateIndexIfNotExists("idx_sessions_last_activity_at", "Sessions", "LastActivityAt") 49 } 50 51 func (me SqlSessionStore) Save(session *model.Session) (*model.Session, error) { 52 if session.Id != "" { 53 return nil, store.NewErrInvalidInput("Session", "id", session.Id) 54 } 55 session.PreSave() 56 57 if err := me.GetMaster().Insert(session); err != nil { 58 return nil, errors.Wrapf(err, "failed to save Session with id=%s", session.Id) 59 } 60 61 teamMembers, err := me.Team().GetTeamsForUser(context.Background(), session.UserId) 62 if err != nil { 63 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) 64 } 65 66 session.TeamMembers = make([]*model.TeamMember, 0, len(teamMembers)) 67 for _, tm := range teamMembers { 68 if tm.DeleteAt == 0 { 69 session.TeamMembers = append(session.TeamMembers, tm) 70 } 71 } 72 73 return session, nil 74 } 75 76 func (me SqlSessionStore) Get(sessionIdOrToken string) (*model.Session, error) { 77 var sessions []*model.Session 78 79 if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE Token = :Token OR Id = :Id LIMIT 1", map[string]interface{}{"Token": sessionIdOrToken, "Id": sessionIdOrToken}); err != nil { 80 return nil, errors.Wrapf(err, "failed to find Sessions with sessionIdOrToken=%s", sessionIdOrToken) 81 } else if len(sessions) == 0 { 82 return nil, store.NewErrNotFound("Session", fmt.Sprintf("sessionIdOrToken=%s", sessionIdOrToken)) 83 } 84 session := sessions[0] 85 86 tempMembers, err := me.Team().GetTeamsForUser( 87 WithMaster(context.Background()), 88 session.UserId) 89 if err != nil { 90 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) 91 } 92 sessions[0].TeamMembers = make([]*model.TeamMember, 0, len(tempMembers)) 93 for _, tm := range tempMembers { 94 if tm.DeleteAt == 0 { 95 sessions[0].TeamMembers = append(sessions[0].TeamMembers, tm) 96 } 97 } 98 return session, nil 99 } 100 101 func (me SqlSessionStore) GetSessions(userId string) ([]*model.Session, error) { 102 var sessions []*model.Session 103 104 if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE UserId = :UserId ORDER BY LastActivityAt DESC", map[string]interface{}{"UserId": userId}); err != nil { 105 return nil, errors.Wrapf(err, "failed to find Sessions with userId=%s", userId) 106 } 107 108 teamMembers, err := me.Team().GetTeamsForUser(context.Background(), userId) 109 if err != nil { 110 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", userId) 111 } 112 113 for _, session := range sessions { 114 session.TeamMembers = make([]*model.TeamMember, 0, len(teamMembers)) 115 for _, tm := range teamMembers { 116 if tm.DeleteAt == 0 { 117 session.TeamMembers = append(session.TeamMembers, tm) 118 } 119 } 120 } 121 return sessions, nil 122 } 123 124 func (me SqlSessionStore) GetSessionsWithActiveDeviceIds(userId string) ([]*model.Session, error) { 125 query := 126 `SELECT * 127 FROM 128 Sessions 129 WHERE 130 UserId = :UserId AND 131 ExpiresAt != 0 AND 132 :ExpiresAt <= ExpiresAt AND 133 DeviceId != ''` 134 135 var sessions []*model.Session 136 137 _, err := me.GetReplica().Select(&sessions, query, map[string]interface{}{"UserId": userId, "ExpiresAt": model.GetMillis()}) 138 if err != nil { 139 return nil, errors.Wrapf(err, "failed to find Sessions with userId=%s", userId) 140 } 141 return sessions, nil 142 } 143 144 func (me SqlSessionStore) GetSessionsExpired(thresholdMillis int64, mobileOnly bool, unnotifiedOnly bool) ([]*model.Session, error) { 145 now := model.GetMillis() 146 builder := me.getQueryBuilder(). 147 Select("*"). 148 From("Sessions"). 149 Where(sq.NotEq{"ExpiresAt": 0}). 150 Where(sq.Lt{"ExpiresAt": now}). 151 Where(sq.Gt{"ExpiresAt": now - thresholdMillis}) 152 if mobileOnly { 153 builder = builder.Where(sq.NotEq{"DeviceId": ""}) 154 } 155 if unnotifiedOnly { 156 builder = builder.Where(sq.NotEq{"ExpiredNotify": true}) 157 } 158 159 query, args, err := builder.ToSql() 160 if err != nil { 161 return nil, errors.Wrap(err, "sessions_tosql") 162 } 163 164 var sessions []*model.Session 165 166 _, err = me.GetReplica().Select(&sessions, query, args...) 167 if err != nil { 168 return nil, errors.Wrap(err, "failed to find Sessions") 169 } 170 return sessions, nil 171 } 172 173 func (me SqlSessionStore) UpdateExpiredNotify(sessionId string, notified bool) error { 174 query, args, err := me.getQueryBuilder(). 175 Update("Sessions"). 176 Set("ExpiredNotify", notified). 177 Where(sq.Eq{"Id": sessionId}). 178 ToSql() 179 if err != nil { 180 return errors.Wrap(err, "sessions_tosql") 181 } 182 183 _, err = me.GetMaster().Exec(query, args...) 184 if err != nil { 185 return errors.Wrapf(err, "failed to update Session with id=%s", sessionId) 186 } 187 return nil 188 } 189 190 func (me SqlSessionStore) Remove(sessionIdOrToken string) error { 191 _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = :Id Or Token = :Token", map[string]interface{}{"Id": sessionIdOrToken, "Token": sessionIdOrToken}) 192 if err != nil { 193 return errors.Wrapf(err, "failed to delete Session with sessionIdOrToken=%s", sessionIdOrToken) 194 } 195 return nil 196 } 197 198 func (me SqlSessionStore) RemoveAllSessions() error { 199 _, err := me.GetMaster().Exec("DELETE FROM Sessions") 200 if err != nil { 201 return errors.Wrap(err, "failed to delete all Sessions") 202 } 203 return nil 204 } 205 206 func (me SqlSessionStore) PermanentDeleteSessionsByUser(userId string) error { 207 _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) 208 if err != nil { 209 return errors.Wrapf(err, "failed to delete Session with userId=%s", userId) 210 } 211 212 return nil 213 } 214 215 func (me SqlSessionStore) UpdateExpiresAt(sessionId string, time int64) error { 216 _, err := me.GetMaster().Exec("UPDATE Sessions SET ExpiresAt = :ExpiresAt, ExpiredNotify = false WHERE Id = :Id", map[string]interface{}{"ExpiresAt": time, "Id": sessionId}) 217 if err != nil { 218 return errors.Wrapf(err, "failed to update Session with sessionId=%s", sessionId) 219 } 220 return nil 221 } 222 223 func (me SqlSessionStore) UpdateLastActivityAt(sessionId string, time int64) error { 224 _, err := me.GetMaster().Exec("UPDATE Sessions SET LastActivityAt = :LastActivityAt WHERE Id = :Id", map[string]interface{}{"LastActivityAt": time, "Id": sessionId}) 225 if err != nil { 226 return errors.Wrapf(err, "failed to update Session with id=%s", sessionId) 227 } 228 return nil 229 } 230 231 func (me SqlSessionStore) UpdateRoles(userId, roles string) (string, error) { 232 query := "UPDATE Sessions SET Roles = :Roles WHERE UserId = :UserId" 233 234 _, err := me.GetMaster().Exec(query, map[string]interface{}{"Roles": roles, "UserId": userId}) 235 if err != nil { 236 return "", errors.Wrapf(err, "failed to update Session with userId=%s and roles=%s", userId, roles) 237 } 238 return userId, nil 239 } 240 241 func (me SqlSessionStore) UpdateDeviceId(id string, deviceId string, expiresAt int64) (string, error) { 242 query := "UPDATE Sessions SET DeviceId = :DeviceId, ExpiresAt = :ExpiresAt, ExpiredNotify = false WHERE Id = :Id" 243 244 _, err := me.GetMaster().Exec(query, map[string]interface{}{"DeviceId": deviceId, "Id": id, "ExpiresAt": expiresAt}) 245 if err != nil { 246 return "", errors.Wrapf(err, "failed to update Session with id=%s", id) 247 } 248 return deviceId, nil 249 } 250 251 func (me SqlSessionStore) UpdateProps(session *model.Session) error { 252 oldSession, err := me.Get(session.Id) 253 if err != nil { 254 return err 255 } 256 oldSession.Props = session.Props 257 258 count, err := me.GetMaster().Update(oldSession) 259 if err != nil { 260 return errors.Wrap(err, "failed to update Session") 261 } 262 if count != 1 { 263 return fmt.Errorf("updated Sessions were %d, expected 1", count) 264 } 265 return nil 266 } 267 268 func (me SqlSessionStore) AnalyticsSessionCount() (int64, error) { 269 query := 270 `SELECT 271 COUNT(*) 272 FROM 273 Sessions 274 WHERE ExpiresAt > :Time` 275 count, err := me.GetReplica().SelectInt(query, map[string]interface{}{"Time": model.GetMillis()}) 276 if err != nil { 277 return int64(0), errors.Wrap(err, "failed to count Sessions") 278 } 279 return count, nil 280 } 281 282 func (me SqlSessionStore) Cleanup(expiryTime int64, batchSize int64) { 283 mlog.Debug("Cleaning up session store.") 284 285 var query string 286 if me.DriverName() == model.DATABASE_DRIVER_POSTGRES { 287 query = "DELETE FROM Sessions WHERE Id = any (array (SELECT Id FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit))" 288 } else { 289 query = "DELETE FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit" 290 } 291 292 var rowsAffected int64 = 1 293 294 for rowsAffected > 0 { 295 sqlResult, err := me.GetMaster().Exec(query, map[string]interface{}{"ExpiresAt": expiryTime, "Limit": batchSize}) 296 if err != nil { 297 mlog.Error("Unable to cleanup session store.", mlog.Err(err)) 298 return 299 } 300 var rowErr error 301 rowsAffected, rowErr = sqlResult.RowsAffected() 302 if rowErr != nil { 303 mlog.Error("Unable to cleanup session store.", mlog.Err(err)) 304 return 305 } 306 307 time.Sleep(SessionsCleanupDelayMilliseconds * time.Millisecond) 308 } 309 }