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 }