github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/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  	"net/http"
     8  	"time"
     9  
    10  	l4g "github.com/alecthomas/log4go"
    11  	"github.com/mattermost/mattermost-server/model"
    12  	"github.com/mattermost/mattermost-server/store"
    13  )
    14  
    15  const (
    16  	SESSIONS_CLEANUP_DELAY_MILLISECONDS = 100
    17  )
    18  
    19  type SqlSessionStore struct {
    20  	SqlStore
    21  }
    22  
    23  func NewSqlSessionStore(sqlStore SqlStore) store.SessionStore {
    24  	us := &SqlSessionStore{sqlStore}
    25  
    26  	for _, db := range sqlStore.GetAllConns() {
    27  		table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id")
    28  		table.ColMap("Id").SetMaxSize(26)
    29  		table.ColMap("Token").SetMaxSize(26)
    30  		table.ColMap("UserId").SetMaxSize(26)
    31  		table.ColMap("DeviceId").SetMaxSize(512)
    32  		table.ColMap("Roles").SetMaxSize(64)
    33  		table.ColMap("Props").SetMaxSize(1000)
    34  	}
    35  
    36  	return us
    37  }
    38  
    39  func (me SqlSessionStore) CreateIndexesIfNotExists() {
    40  	me.CreateIndexIfNotExists("idx_sessions_user_id", "Sessions", "UserId")
    41  	me.CreateIndexIfNotExists("idx_sessions_token", "Sessions", "Token")
    42  	me.CreateIndexIfNotExists("idx_sessions_expires_at", "Sessions", "ExpiresAt")
    43  	me.CreateIndexIfNotExists("idx_sessions_create_at", "Sessions", "CreateAt")
    44  	me.CreateIndexIfNotExists("idx_sessions_last_activity_at", "Sessions", "LastActivityAt")
    45  }
    46  
    47  func (me SqlSessionStore) Save(session *model.Session) store.StoreChannel {
    48  	return store.Do(func(result *store.StoreResult) {
    49  		if len(session.Id) > 0 {
    50  			result.Err = model.NewAppError("SqlSessionStore.Save", "store.sql_session.save.existing.app_error", nil, "id="+session.Id, http.StatusBadRequest)
    51  			return
    52  		}
    53  
    54  		session.PreSave()
    55  
    56  		tcs := me.Team().GetTeamsForUser(session.UserId)
    57  
    58  		if err := me.GetMaster().Insert(session); err != nil {
    59  			result.Err = model.NewAppError("SqlSessionStore.Save", "store.sql_session.save.app_error", nil, "id="+session.Id+", "+err.Error(), http.StatusInternalServerError)
    60  			return
    61  		} else {
    62  			result.Data = session
    63  		}
    64  
    65  		if rtcs := <-tcs; rtcs.Err != nil {
    66  			result.Err = model.NewAppError("SqlSessionStore.Save", "store.sql_session.save.app_error", nil, "id="+session.Id+", "+rtcs.Err.Error(), http.StatusInternalServerError)
    67  			return
    68  		} else {
    69  			tempMembers := rtcs.Data.([]*model.TeamMember)
    70  			session.TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
    71  			for _, tm := range tempMembers {
    72  				if tm.DeleteAt == 0 {
    73  					session.TeamMembers = append(session.TeamMembers, tm)
    74  				}
    75  			}
    76  		}
    77  	})
    78  }
    79  
    80  func (me SqlSessionStore) Get(sessionIdOrToken string) store.StoreChannel {
    81  	return store.Do(func(result *store.StoreResult) {
    82  		var sessions []*model.Session
    83  
    84  		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 {
    85  			result.Err = model.NewAppError("SqlSessionStore.Get", "store.sql_session.get.app_error", nil, "sessionIdOrToken="+sessionIdOrToken+", "+err.Error(), http.StatusInternalServerError)
    86  		} else if len(sessions) == 0 {
    87  			result.Err = model.NewAppError("SqlSessionStore.Get", "store.sql_session.get.app_error", nil, "sessionIdOrToken="+sessionIdOrToken, http.StatusNotFound)
    88  		} else {
    89  			result.Data = sessions[0]
    90  
    91  			tcs := me.Team().GetTeamsForUser(sessions[0].UserId)
    92  			if rtcs := <-tcs; rtcs.Err != nil {
    93  				result.Err = model.NewAppError("SqlSessionStore.Get", "store.sql_session.get.app_error", nil, "sessionIdOrToken="+sessionIdOrToken+", "+rtcs.Err.Error(), http.StatusInternalServerError)
    94  				return
    95  			} else {
    96  				tempMembers := rtcs.Data.([]*model.TeamMember)
    97  				sessions[0].TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
    98  				for _, tm := range tempMembers {
    99  					if tm.DeleteAt == 0 {
   100  						sessions[0].TeamMembers = append(sessions[0].TeamMembers, tm)
   101  					}
   102  				}
   103  			}
   104  		}
   105  	})
   106  }
   107  
   108  func (me SqlSessionStore) GetSessions(userId string) store.StoreChannel {
   109  	return store.Do(func(result *store.StoreResult) {
   110  		var sessions []*model.Session
   111  
   112  		tcs := me.Team().GetTeamsForUser(userId)
   113  
   114  		if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE UserId = :UserId ORDER BY LastActivityAt DESC", map[string]interface{}{"UserId": userId}); err != nil {
   115  			result.Err = model.NewAppError("SqlSessionStore.GetSessions", "store.sql_session.get_sessions.app_error", nil, err.Error(), http.StatusInternalServerError)
   116  		} else {
   117  
   118  			result.Data = sessions
   119  		}
   120  
   121  		if rtcs := <-tcs; rtcs.Err != nil {
   122  			result.Err = model.NewAppError("SqlSessionStore.GetSessions", "store.sql_session.get_sessions.app_error", nil, rtcs.Err.Error(), http.StatusInternalServerError)
   123  			return
   124  		} else {
   125  			for _, session := range sessions {
   126  				tempMembers := rtcs.Data.([]*model.TeamMember)
   127  				session.TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
   128  				for _, tm := range tempMembers {
   129  					if tm.DeleteAt == 0 {
   130  						session.TeamMembers = append(session.TeamMembers, tm)
   131  					}
   132  				}
   133  			}
   134  		}
   135  	})
   136  }
   137  
   138  func (me SqlSessionStore) GetSessionsWithActiveDeviceIds(userId string) store.StoreChannel {
   139  	return store.Do(func(result *store.StoreResult) {
   140  		var sessions []*model.Session
   141  
   142  		if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE UserId = :UserId AND ExpiresAt != 0 AND :ExpiresAt <= ExpiresAt AND DeviceId != ''", map[string]interface{}{"UserId": userId, "ExpiresAt": model.GetMillis()}); err != nil {
   143  			result.Err = model.NewAppError("SqlSessionStore.GetActiveSessionsWithDeviceIds", "store.sql_session.get_sessions.app_error", nil, err.Error(), http.StatusInternalServerError)
   144  		} else {
   145  
   146  			result.Data = sessions
   147  		}
   148  	})
   149  }
   150  
   151  func (me SqlSessionStore) Remove(sessionIdOrToken string) store.StoreChannel {
   152  	return store.Do(func(result *store.StoreResult) {
   153  		_, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = :Id Or Token = :Token", map[string]interface{}{"Id": sessionIdOrToken, "Token": sessionIdOrToken})
   154  		if err != nil {
   155  			result.Err = model.NewAppError("SqlSessionStore.RemoveSession", "store.sql_session.remove.app_error", nil, "id="+sessionIdOrToken+", err="+err.Error(), http.StatusInternalServerError)
   156  		}
   157  	})
   158  }
   159  
   160  func (me SqlSessionStore) RemoveAllSessions() store.StoreChannel {
   161  	return store.Do(func(result *store.StoreResult) {
   162  		_, err := me.GetMaster().Exec("DELETE FROM Sessions")
   163  		if err != nil {
   164  			result.Err = model.NewAppError("SqlSessionStore.RemoveAllSessions", "store.sql_session.remove_all_sessions_for_team.app_error", nil, err.Error(), http.StatusInternalServerError)
   165  		}
   166  	})
   167  }
   168  
   169  func (me SqlSessionStore) PermanentDeleteSessionsByUser(userId string) store.StoreChannel {
   170  	return store.Do(func(result *store.StoreResult) {
   171  		_, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE UserId = :UserId", map[string]interface{}{"UserId": userId})
   172  		if err != nil {
   173  			result.Err = model.NewAppError("SqlSessionStore.RemoveAllSessionsForUser", "store.sql_session.permanent_delete_sessions_by_user.app_error", nil, "id="+userId+", err="+err.Error(), http.StatusInternalServerError)
   174  		}
   175  	})
   176  }
   177  
   178  func (me SqlSessionStore) UpdateLastActivityAt(sessionId string, time int64) store.StoreChannel {
   179  	return store.Do(func(result *store.StoreResult) {
   180  		if _, err := me.GetMaster().Exec("UPDATE Sessions SET LastActivityAt = :LastActivityAt WHERE Id = :Id", map[string]interface{}{"LastActivityAt": time, "Id": sessionId}); err != nil {
   181  			result.Err = model.NewAppError("SqlSessionStore.UpdateLastActivityAt", "store.sql_session.update_last_activity.app_error", nil, "sessionId="+sessionId, http.StatusInternalServerError)
   182  		} else {
   183  			result.Data = sessionId
   184  		}
   185  	})
   186  }
   187  
   188  func (me SqlSessionStore) UpdateRoles(userId, roles string) store.StoreChannel {
   189  	return store.Do(func(result *store.StoreResult) {
   190  		if _, err := me.GetMaster().Exec("UPDATE Sessions SET Roles = :Roles WHERE UserId = :UserId", map[string]interface{}{"Roles": roles, "UserId": userId}); err != nil {
   191  			result.Err = model.NewAppError("SqlSessionStore.UpdateRoles", "store.sql_session.update_roles.app_error", nil, "userId="+userId, http.StatusInternalServerError)
   192  		} else {
   193  			result.Data = userId
   194  		}
   195  	})
   196  }
   197  
   198  func (me SqlSessionStore) UpdateDeviceId(id string, deviceId string, expiresAt int64) store.StoreChannel {
   199  	return store.Do(func(result *store.StoreResult) {
   200  		if _, err := me.GetMaster().Exec("UPDATE Sessions SET DeviceId = :DeviceId, ExpiresAt = :ExpiresAt WHERE Id = :Id", map[string]interface{}{"DeviceId": deviceId, "Id": id, "ExpiresAt": expiresAt}); err != nil {
   201  			result.Err = model.NewAppError("SqlSessionStore.UpdateDeviceId", "store.sql_session.update_device_id.app_error", nil, err.Error(), http.StatusInternalServerError)
   202  		} else {
   203  			result.Data = deviceId
   204  		}
   205  	})
   206  }
   207  
   208  func (me SqlSessionStore) AnalyticsSessionCount() store.StoreChannel {
   209  	return store.Do(func(result *store.StoreResult) {
   210  		query :=
   211  			`SELECT
   212                  COUNT(*)
   213              FROM
   214                  Sessions
   215              WHERE ExpiresAt > :Time`
   216  
   217  		if c, err := me.GetReplica().SelectInt(query, map[string]interface{}{"Time": model.GetMillis()}); err != nil {
   218  			result.Err = model.NewAppError("SqlSessionStore.AnalyticsSessionCount", "store.sql_session.analytics_session_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   219  		} else {
   220  			result.Data = c
   221  		}
   222  	})
   223  }
   224  
   225  func (me SqlSessionStore) Cleanup(expiryTime int64, batchSize int64) {
   226  	l4g.Debug("Cleaning up session store.")
   227  
   228  	var query string
   229  	if me.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   230  		query = "DELETE FROM Sessions WHERE Id = any (array (SELECT Id FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit))"
   231  	} else {
   232  		query = "DELETE FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit"
   233  	}
   234  
   235  	var rowsAffected int64 = 1
   236  
   237  	for rowsAffected > 0 {
   238  		if sqlResult, err := me.GetMaster().Exec(query, map[string]interface{}{"ExpiresAt": expiryTime, "Limit": batchSize}); err != nil {
   239  			l4g.Error("Unable to cleanup session store. err=%v", err.Error())
   240  			return
   241  		} else {
   242  			var rowErr error
   243  			rowsAffected, rowErr = sqlResult.RowsAffected()
   244  			if rowErr != nil {
   245  				l4g.Error("Unable to cleanup session store. err=%v", err.Error())
   246  				return
   247  			}
   248  		}
   249  
   250  		time.Sleep(SESSIONS_CLEANUP_DELAY_MILLISECONDS * time.Millisecond)
   251  	}
   252  }