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  }