github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/session_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/mattermost/mattermost-server/v5/model"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestCache(t *testing.T) {
    17  	th := Setup(t)
    18  	defer th.TearDown()
    19  
    20  	session := &model.Session{
    21  		Id:     model.NewId(),
    22  		Token:  model.NewId(),
    23  		UserId: model.NewId(),
    24  	}
    25  
    26  	session2 := &model.Session{
    27  		Id:     model.NewId(),
    28  		Token:  model.NewId(),
    29  		UserId: model.NewId(),
    30  	}
    31  
    32  	th.App.Srv().sessionCache.SetWithExpiry(session.Token, session, 5*time.Minute)
    33  	th.App.Srv().sessionCache.SetWithExpiry(session2.Token, session2, 5*time.Minute)
    34  
    35  	keys, err := th.App.Srv().sessionCache.Keys()
    36  	require.Nil(t, err)
    37  	require.NotEmpty(t, keys)
    38  
    39  	th.App.ClearSessionCacheForUser(session.UserId)
    40  
    41  	rkeys, err := th.App.Srv().sessionCache.Keys()
    42  	require.Nil(t, err)
    43  	require.Lenf(t, rkeys, len(keys)-1, "should have one less: %d - %d != 1", len(keys), len(rkeys))
    44  	require.NotEmpty(t, rkeys)
    45  
    46  	th.App.ClearSessionCacheForAllUsers()
    47  
    48  	rkeys, err = th.App.Srv().sessionCache.Keys()
    49  	require.Nil(t, err)
    50  	require.Empty(t, rkeys)
    51  }
    52  
    53  func TestGetSessionIdleTimeoutInMinutes(t *testing.T) {
    54  	th := Setup(t)
    55  	defer th.TearDown()
    56  
    57  	session := &model.Session{
    58  		UserId: model.NewId(),
    59  	}
    60  
    61  	session, _ = th.App.CreateSession(session)
    62  
    63  	th.App.Srv().SetLicense(model.NewTestLicense("compliance"))
    64  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 5 })
    65  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ExtendSessionLengthWithActivity = false })
    66  
    67  	rsession, err := th.App.GetSession(session.Token)
    68  	require.Nil(t, err)
    69  	assert.Equal(t, rsession.Id, session.Id)
    70  
    71  	// Test regular session, should timeout
    72  	time := session.LastActivityAt - (1000 * 60 * 6)
    73  	nErr := th.App.Srv().Store.Session().UpdateLastActivityAt(session.Id, time)
    74  	require.Nil(t, nErr)
    75  	th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
    76  
    77  	rsession, err = th.App.GetSession(session.Token)
    78  	require.NotNil(t, err)
    79  	assert.Equal(t, "api.context.invalid_token.error", err.Id)
    80  	assert.Equal(t, "idle timeout", err.DetailedError)
    81  	assert.Nil(t, rsession)
    82  
    83  	// Test oauth session, should not timeout
    84  	session = &model.Session{
    85  		UserId:  model.NewId(),
    86  		IsOAuth: true,
    87  	}
    88  
    89  	session, _ = th.App.CreateSession(session)
    90  	time = session.LastActivityAt - (1000 * 60 * 6)
    91  	nErr = th.App.Srv().Store.Session().UpdateLastActivityAt(session.Id, time)
    92  	require.Nil(t, nErr)
    93  	th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
    94  
    95  	_, err = th.App.GetSession(session.Token)
    96  	assert.Nil(t, err)
    97  
    98  	// Test personal access token session, should not timeout
    99  	session = &model.Session{
   100  		UserId: model.NewId(),
   101  	}
   102  	session.AddProp(model.SESSION_PROP_TYPE, model.SESSION_TYPE_USER_ACCESS_TOKEN)
   103  
   104  	session, _ = th.App.CreateSession(session)
   105  	time = session.LastActivityAt - (1000 * 60 * 6)
   106  	nErr = th.App.Srv().Store.Session().UpdateLastActivityAt(session.Id, time)
   107  	require.Nil(t, nErr)
   108  	th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
   109  
   110  	_, err = th.App.GetSession(session.Token)
   111  	assert.Nil(t, err)
   112  
   113  	th.App.Srv().SetLicense(model.NewTestLicense("compliance"))
   114  
   115  	// Test regular session with timeout set to 0, should not timeout
   116  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 0 })
   117  
   118  	session = &model.Session{
   119  		UserId: model.NewId(),
   120  	}
   121  
   122  	session, _ = th.App.CreateSession(session)
   123  	time = session.LastActivityAt - (1000 * 60 * 6)
   124  	nErr = th.App.Srv().Store.Session().UpdateLastActivityAt(session.Id, time)
   125  	require.Nil(t, nErr)
   126  	th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
   127  
   128  	_, err = th.App.GetSession(session.Token)
   129  	assert.Nil(t, err)
   130  }
   131  
   132  func TestUpdateSessionOnPromoteDemote(t *testing.T) {
   133  	th := Setup(t).InitBasic()
   134  	defer th.TearDown()
   135  
   136  	th.App.Srv().SetLicense(model.NewTestLicense())
   137  
   138  	t.Run("Promote Guest to User updates the session", func(t *testing.T) {
   139  		guest := th.CreateGuest()
   140  
   141  		session, err := th.App.CreateSession(&model.Session{UserId: guest.Id, Props: model.StringMap{model.SESSION_PROP_IS_GUEST: "true"}})
   142  		require.Nil(t, err)
   143  
   144  		rsession, err := th.App.GetSession(session.Token)
   145  		require.Nil(t, err)
   146  		assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
   147  
   148  		err = th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
   149  		require.Nil(t, err)
   150  
   151  		rsession, err = th.App.GetSession(session.Token)
   152  		require.Nil(t, err)
   153  		assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
   154  
   155  		th.App.ClearSessionCacheForUser(session.UserId)
   156  
   157  		rsession, err = th.App.GetSession(session.Token)
   158  		require.Nil(t, err)
   159  		assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
   160  	})
   161  
   162  	t.Run("Demote User to Guest updates the session", func(t *testing.T) {
   163  		user := th.CreateUser()
   164  
   165  		session, err := th.App.CreateSession(&model.Session{UserId: user.Id, Props: model.StringMap{model.SESSION_PROP_IS_GUEST: "false"}})
   166  		require.Nil(t, err)
   167  
   168  		rsession, err := th.App.GetSession(session.Token)
   169  		require.Nil(t, err)
   170  		assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
   171  
   172  		err = th.App.DemoteUserToGuest(user)
   173  		require.Nil(t, err)
   174  
   175  		rsession, err = th.App.GetSession(session.Token)
   176  		require.Nil(t, err)
   177  		assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
   178  
   179  		th.App.ClearSessionCacheForUser(session.UserId)
   180  		rsession, err = th.App.GetSession(session.Token)
   181  		require.Nil(t, err)
   182  		assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
   183  	})
   184  }
   185  
   186  const hourMillis int64 = 60 * 60 * 1000
   187  const dayMillis int64 = 24 * hourMillis
   188  
   189  func TestApp_GetSessionLengthInMillis(t *testing.T) {
   190  	th := Setup(t)
   191  	defer th.TearDown()
   192  
   193  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthMobileInDays = 3 })
   194  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthSSOInDays = 2 })
   195  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthWebInDays = 1 })
   196  
   197  	t.Run("get session length mobile", func(t *testing.T) {
   198  		session := &model.Session{
   199  			UserId:   model.NewId(),
   200  			DeviceId: model.NewId(),
   201  		}
   202  		session, err := th.App.CreateSession(session)
   203  		require.Nil(t, err)
   204  
   205  		sessionLength := th.App.GetSessionLengthInMillis(session)
   206  		require.Equal(t, dayMillis*3, sessionLength)
   207  	})
   208  
   209  	t.Run("get session length mobile when isMobile in props is set", func(t *testing.T) {
   210  		session := &model.Session{
   211  			UserId: model.NewId(),
   212  			Props: map[string]string{
   213  				model.USER_AUTH_SERVICE_IS_MOBILE: "true",
   214  			},
   215  		}
   216  		session, err := th.App.CreateSession(session)
   217  		require.Nil(t, err)
   218  
   219  		sessionLength := th.App.GetSessionLengthInMillis(session)
   220  		require.Equal(t, dayMillis*3, sessionLength)
   221  	})
   222  
   223  	t.Run("get session length mobile when isMobile in props is set and takes priority over saml", func(t *testing.T) {
   224  		session := &model.Session{
   225  			UserId: model.NewId(),
   226  			Props: map[string]string{
   227  				model.USER_AUTH_SERVICE_IS_MOBILE: "true",
   228  				model.USER_AUTH_SERVICE_IS_SAML:   "true",
   229  			},
   230  		}
   231  		session, err := th.App.CreateSession(session)
   232  		require.Nil(t, err)
   233  
   234  		sessionLength := th.App.GetSessionLengthInMillis(session)
   235  		require.Equal(t, dayMillis*3, sessionLength)
   236  	})
   237  
   238  	t.Run("get session length SSO", func(t *testing.T) {
   239  		session := &model.Session{
   240  			UserId:  model.NewId(),
   241  			IsOAuth: true,
   242  		}
   243  		session, err := th.App.CreateSession(session)
   244  		require.Nil(t, err)
   245  
   246  		sessionLength := th.App.GetSessionLengthInMillis(session)
   247  		require.Equal(t, dayMillis*2, sessionLength)
   248  	})
   249  
   250  	t.Run("get session length SSO using props", func(t *testing.T) {
   251  		session := &model.Session{
   252  			UserId: model.NewId(),
   253  			Props: map[string]string{
   254  				model.USER_AUTH_SERVICE_IS_SAML: "true",
   255  			}}
   256  		session, err := th.App.CreateSession(session)
   257  		require.Nil(t, err)
   258  
   259  		sessionLength := th.App.GetSessionLengthInMillis(session)
   260  		require.Equal(t, dayMillis*2, sessionLength)
   261  	})
   262  
   263  	t.Run("get session length web/LDAP", func(t *testing.T) {
   264  		session := &model.Session{
   265  			UserId: model.NewId(),
   266  		}
   267  		session, err := th.App.CreateSession(session)
   268  		require.Nil(t, err)
   269  
   270  		sessionLength := th.App.GetSessionLengthInMillis(session)
   271  		require.Equal(t, dayMillis*1, sessionLength)
   272  	})
   273  }
   274  
   275  func TestApp_ExtendExpiryIfNeeded(t *testing.T) {
   276  	th := Setup(t)
   277  	defer th.TearDown()
   278  
   279  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ExtendSessionLengthWithActivity = true })
   280  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthMobileInDays = 3 })
   281  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthSSOInDays = 2 })
   282  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionLengthWebInDays = 1 })
   283  
   284  	t.Run("expired session should not be extended", func(t *testing.T) {
   285  		expires := model.GetMillis() - hourMillis
   286  		session := &model.Session{
   287  			UserId:    model.NewId(),
   288  			ExpiresAt: expires,
   289  		}
   290  		session, err := th.App.CreateSession(session)
   291  		require.Nil(t, err)
   292  
   293  		ok := th.App.ExtendSessionExpiryIfNeeded(session)
   294  
   295  		require.False(t, ok)
   296  		require.Equal(t, expires, session.ExpiresAt)
   297  		require.True(t, session.IsExpired())
   298  	})
   299  
   300  	t.Run("session within threshold should not be extended", func(t *testing.T) {
   301  		session := &model.Session{
   302  			UserId: model.NewId(),
   303  		}
   304  		session, err := th.App.CreateSession(session)
   305  		require.Nil(t, err)
   306  
   307  		expires := model.GetMillis() + th.App.GetSessionLengthInMillis(session)
   308  		session.ExpiresAt = expires
   309  
   310  		ok := th.App.ExtendSessionExpiryIfNeeded(session)
   311  
   312  		require.False(t, ok)
   313  		require.Equal(t, expires, session.ExpiresAt)
   314  		require.False(t, session.IsExpired())
   315  	})
   316  
   317  	var tests = []struct {
   318  		name    string
   319  		session *model.Session
   320  	}{
   321  		{name: "mobile", session: &model.Session{UserId: model.NewId(), DeviceId: model.NewId(), Token: model.NewId()}},
   322  		{name: "SSO", session: &model.Session{UserId: model.NewId(), IsOAuth: true, Token: model.NewId()}},
   323  		{name: "web/LDAP", session: &model.Session{UserId: model.NewId(), Token: model.NewId()}},
   324  	}
   325  	for _, test := range tests {
   326  		t.Run(fmt.Sprintf("%s session beyond threshold should update ExpiresAt", test.name), func(t *testing.T) {
   327  			session, err := th.App.CreateSession(test.session)
   328  			require.Nil(t, err)
   329  
   330  			expires := model.GetMillis() + th.App.GetSessionLengthInMillis(session) - hourMillis
   331  			session.ExpiresAt = expires
   332  
   333  			ok := th.App.ExtendSessionExpiryIfNeeded(session)
   334  
   335  			require.True(t, ok)
   336  			require.Greater(t, session.ExpiresAt, expires)
   337  			require.False(t, session.IsExpired())
   338  
   339  			// check cache was updated
   340  			var cachedSession *model.Session
   341  			errGet := th.App.Srv().sessionCache.Get(session.Token, &cachedSession)
   342  			require.Nil(t, errGet)
   343  			require.Equal(t, session.ExpiresAt, cachedSession.ExpiresAt)
   344  
   345  			// check database was updated.
   346  			storedSession, nErr := th.App.Srv().Store.Session().Get(session.Token)
   347  			require.Nil(t, nErr)
   348  			require.Equal(t, session.ExpiresAt, storedSession.ExpiresAt)
   349  		})
   350  	}
   351  
   352  }
   353  
   354  const (
   355  	dayInMillis = 86400000
   356  	grace       = 5 * 1000
   357  	thirtyDays  = dayInMillis * 30
   358  )
   359  
   360  func TestApp_SetSessionExpireInDays(t *testing.T) {
   361  	th := Setup(t)
   362  	defer th.TearDown()
   363  
   364  	now := model.GetMillis()
   365  	createAt := now - (dayInMillis * 20)
   366  
   367  	tests := []struct {
   368  		name   string
   369  		extend bool
   370  		create bool
   371  		days   int
   372  		want   int64
   373  	}{
   374  		{name: "zero days, extend", extend: true, create: true, days: 0, want: now},
   375  		{name: "zero days, extend", extend: true, create: false, days: 0, want: now},
   376  		{name: "zero days, no extend", extend: false, create: true, days: 0, want: createAt},
   377  		{name: "zero days, no extend", extend: false, create: false, days: 0, want: now},
   378  		{name: "thirty days, extend", extend: true, create: true, days: 30, want: now + thirtyDays},
   379  		{name: "thirty days, extend", extend: true, create: false, days: 30, want: now + thirtyDays},
   380  		{name: "thirty days, no extend", extend: false, create: true, days: 30, want: createAt + thirtyDays},
   381  		{name: "thirty days, no extend", extend: false, create: false, days: 30, want: now + thirtyDays},
   382  	}
   383  	for _, tt := range tests {
   384  		t.Run(tt.name, func(t *testing.T) {
   385  			th.App.UpdateConfig(func(cfg *model.Config) {
   386  				*cfg.ServiceSettings.ExtendSessionLengthWithActivity = tt.extend
   387  			})
   388  			var create int64
   389  			if tt.create {
   390  				create = createAt
   391  			}
   392  
   393  			session := &model.Session{
   394  				CreateAt:  create,
   395  				ExpiresAt: model.GetMillis() + dayInMillis,
   396  			}
   397  			th.App.SetSessionExpireInDays(session, tt.days)
   398  
   399  			// must be within 5 seconds of expected time.
   400  			require.GreaterOrEqual(t, session.ExpiresAt, tt.want-grace)
   401  			require.LessOrEqual(t, session.ExpiresAt, tt.want+grace)
   402  		})
   403  	}
   404  }