mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* sends wipe signal ro mobile devices when revoking user sessions there is a session notification type that can be used with no content to indicate the session has been revoked and data should be wiped client side. This complements the use of session typed notifications to indicate expiry of user sessions carrying containing some message. empty push session typed notifications are sent to mobile devices on revoking sessions only when mobile ephemeral mode is enabled. * move send wipe push notifications to goroutines * wraps goroutine launch in Srv().Go() for proper shutdown * query active sessions to wipe only when ephemeral mode is enabled * address review comments * guard to prevent sending wipe notification to removed tokens
730 lines
22 KiB
Go
730 lines
22 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package storetest
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
)
|
|
|
|
const (
|
|
TenMinutes = 600000
|
|
)
|
|
|
|
func TestSessionStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Run serially to prevent interfering with other tests
|
|
testSessionCleanup(t, rctx, ss)
|
|
|
|
t.Run("Save", func(t *testing.T) { testSessionStoreSave(t, rctx, ss) })
|
|
t.Run("SessionGet", func(t *testing.T) { testSessionGet(t, rctx, ss) })
|
|
t.Run("SessionGetWithDeviceId", func(t *testing.T) { testSessionGetWithDeviceId(t, rctx, ss) })
|
|
t.Run("SessionRemove", func(t *testing.T) { testSessionRemove(t, rctx, ss) })
|
|
t.Run("SessionRemoveAll", func(t *testing.T) { testSessionRemoveAll(t, rctx, ss) })
|
|
t.Run("SessionRemoveByUser", func(t *testing.T) { testSessionRemoveByUser(t, rctx, ss) })
|
|
t.Run("SessionRemoveToken", func(t *testing.T) { testSessionRemoveToken(t, rctx, ss) })
|
|
t.Run("SessionUpdateDeviceId", func(t *testing.T) { testSessionUpdateDeviceId(t, rctx, ss) })
|
|
t.Run("SessionUpdateDeviceId2", func(t *testing.T) { testSessionUpdateDeviceId2(t, rctx, ss) })
|
|
t.Run("SessionUpdateDeviceIdClearsOnEmpty", func(t *testing.T) { testSessionUpdateDeviceIdClearsOnEmpty(t, rctx, ss) })
|
|
t.Run("UpdateExpiresAt", func(t *testing.T) { testSessionStoreUpdateExpiresAt(t, rctx, ss) })
|
|
t.Run("UpdateLastActivityAt", func(t *testing.T) { testSessionStoreUpdateLastActivityAt(t, rctx, ss) })
|
|
t.Run("SessionCount", func(t *testing.T) { testSessionCount(t, rctx, ss) })
|
|
t.Run("GetSessionsExpired", func(t *testing.T) { testGetSessionsExpired(t, rctx, ss) })
|
|
t.Run("UpdateExpiredNotify", func(t *testing.T) { testUpdateExpiredNotify(t, rctx, ss) })
|
|
t.Run("GetLRUSessions", func(t *testing.T) { testGetLRUSessions(t, rctx, ss) })
|
|
t.Run("GetSessionsWithActiveDeviceIds", func(t *testing.T) { testGetSessionsWithActiveDeviceIds(t, rctx, ss) })
|
|
t.Run("GetAllSessionsWithActiveDeviceIds", func(t *testing.T) { testGetAllSessionsWithActiveDeviceIds(t, rctx, ss) })
|
|
t.Run("GetMobileSessionMetadata", func(t *testing.T) { testGetMobileSessionMetadata(t, rctx, ss) })
|
|
}
|
|
|
|
func testSessionStoreSave(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
_, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testSessionGet(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = s1.UserId
|
|
|
|
_, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = s1.UserId
|
|
s3.ExpiresAt = 1
|
|
|
|
_, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Id, s1.Id, "should match")
|
|
|
|
session.Props[model.SessionPropOs] = "linux"
|
|
session.Props[model.SessionPropBrowser] = "Chrome"
|
|
err = ss.Session().UpdateProps(session)
|
|
require.NoError(t, err)
|
|
|
|
session2, err := ss.Session().Get(rctx, session.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Props, session2.Props, "should match")
|
|
|
|
data, err := ss.Session().GetSessions(rctx, s1.UserId)
|
|
require.NoError(t, err)
|
|
require.Len(t, data, 3, "should match len")
|
|
}
|
|
|
|
func testSessionGetWithDeviceId(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
s1.ExpiresAt = model.GetMillis() + 10000
|
|
s1.Props = model.StringMap{}
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = s1.UserId
|
|
s2.DeviceId = model.NewId()
|
|
s2.ExpiresAt = model.GetMillis() + 10000
|
|
s2.Props = model.StringMap{}
|
|
|
|
_, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = s1.UserId
|
|
s3.ExpiresAt = 1
|
|
s3.DeviceId = model.NewId()
|
|
s3.Props = model.StringMap{}
|
|
|
|
_, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
s4 := &model.Session{}
|
|
s4.UserId = s1.UserId
|
|
s4.DeviceId = model.NewId()
|
|
s4.ExpiresAt = model.GetMillis() + 10000
|
|
s4.Props = model.StringMap{
|
|
model.SessionPropLastRemovedDeviceId: s4.DeviceId,
|
|
}
|
|
|
|
_, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
data, err := ss.Session().GetSessionsWithActiveDeviceIds(s1.UserId)
|
|
require.NoError(t, err)
|
|
require.Len(t, data, 1, "should match len")
|
|
}
|
|
|
|
func testSessionRemove(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Id, s1.Id, "should match")
|
|
|
|
removeErr := ss.Session().Remove(s1.Id)
|
|
require.NoError(t, removeErr)
|
|
|
|
_, err = ss.Session().Get(rctx, s1.Id)
|
|
require.Error(t, err, "should have been removed")
|
|
}
|
|
|
|
func testSessionRemoveAll(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Id, s1.Id, "should match")
|
|
|
|
removeErr := ss.Session().RemoveAllSessions()
|
|
require.NoError(t, removeErr)
|
|
|
|
_, err = ss.Session().Get(rctx, s1.Id)
|
|
require.Error(t, err, "should have been removed")
|
|
}
|
|
|
|
func testSessionRemoveByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Id, s1.Id, "should match")
|
|
|
|
deleteErr := ss.Session().PermanentDeleteSessionsByUser(s1.UserId)
|
|
require.NoError(t, deleteErr)
|
|
|
|
_, err = ss.Session().Get(rctx, s1.Id)
|
|
require.Error(t, err, "should have been removed")
|
|
}
|
|
|
|
func testSessionRemoveToken(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, session.Id, s1.Id, "should match")
|
|
|
|
removeErr := ss.Session().Remove(s1.Token)
|
|
require.NoError(t, removeErr)
|
|
|
|
_, err = ss.Session().Get(rctx, s1.Id)
|
|
require.Error(t, err, "should have been removed")
|
|
|
|
data, err := ss.Session().GetSessions(rctx, s1.UserId)
|
|
require.NoError(t, err)
|
|
require.Empty(t, data, "should match len")
|
|
}
|
|
|
|
func testSessionUpdateDeviceId(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateDeviceId(s1.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = model.NewId()
|
|
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateDeviceId(s2.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testSessionUpdateDeviceId2(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateDeviceId(s1.Id, model.PushNotifyAppleReactNative+":1234567890", model.PushNotifyAppleReactNative+":voiptoken", s1.ExpiresAt)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = model.NewId()
|
|
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateDeviceId(s2.Id, model.PushNotifyAppleReactNative+":1234567890", "", s1.ExpiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Verify both columns round-tripped via the regular Get path.
|
|
got, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, model.PushNotifyAppleReactNative+":1234567890", got.DeviceId)
|
|
require.Equal(t, model.PushNotifyAppleReactNative+":voiptoken", got.VoIPDeviceId)
|
|
}
|
|
|
|
func testSessionUpdateDeviceIdClearsOnEmpty(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s := &model.Session{UserId: model.NewId()}
|
|
s, err := ss.Session().Save(rctx, s)
|
|
require.NoError(t, err)
|
|
|
|
standardToken := model.PushNotifyAppleReactNative + ":standard"
|
|
voIPToken := model.PushNotifyAppleReactNative + ":voip"
|
|
err = ss.Session().UpdateDeviceId(s.Id, standardToken, voIPToken, s.ExpiresAt)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateDeviceId(s.Id, "", "", s.ExpiresAt)
|
|
require.NoError(t, err)
|
|
|
|
got, err := ss.Session().Get(rctx, s.Id)
|
|
require.NoError(t, err)
|
|
require.Empty(t, got.DeviceId)
|
|
require.Empty(t, got.VoIPDeviceId)
|
|
}
|
|
|
|
func testSessionStoreUpdateExpiresAt(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateExpiresAt(s1.Id, 1234567890)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, session.ExpiresAt, 1234567890, "ExpiresAt not updated correctly")
|
|
}
|
|
|
|
func testSessionStoreUpdateLastActivityAt(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().UpdateLastActivityAt(s1.Id, 1234567890)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, session.LastActivityAt, 1234567890, "LastActivityAt not updated correctly")
|
|
}
|
|
|
|
func testSessionCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
s1.ExpiresAt = model.GetMillis() + 100000
|
|
|
|
_, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
count, err := ss.Session().AnalyticsSessionCount()
|
|
require.NoError(t, err)
|
|
require.NotZero(t, count, "should have at least 1 session")
|
|
}
|
|
|
|
func testSessionCleanup(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
now := model.GetMillis()
|
|
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
s1.ExpiresAt = 0 // never expires
|
|
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = s1.UserId
|
|
s2.ExpiresAt = now + 1000000 // expires in the future
|
|
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = model.NewId()
|
|
s3.ExpiresAt = 1 // expired
|
|
|
|
s3, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
s4 := &model.Session{}
|
|
s4.UserId = model.NewId()
|
|
s4.ExpiresAt = 2 // expired
|
|
|
|
s4, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.Session().Cleanup(now, 1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Session().Get(rctx, s1.Id)
|
|
assert.NoError(t, err)
|
|
|
|
_, err = ss.Session().Get(rctx, s2.Id)
|
|
assert.NoError(t, err)
|
|
|
|
_, err = ss.Session().Get(rctx, s3.Id)
|
|
assert.Error(t, err)
|
|
|
|
_, err = ss.Session().Get(rctx, s4.Id)
|
|
assert.Error(t, err)
|
|
|
|
removeErr := ss.Session().Remove(s1.Id)
|
|
require.NoError(t, removeErr)
|
|
|
|
removeErr = ss.Session().Remove(s2.Id)
|
|
require.NoError(t, removeErr)
|
|
}
|
|
|
|
func testGetSessionsExpired(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
now := model.GetMillis()
|
|
|
|
// Clear existing sessions.
|
|
err := ss.Session().RemoveAllSessions()
|
|
require.NoError(t, err)
|
|
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
s1.DeviceId = model.NewId()
|
|
s1.ExpiresAt = 0 // never expires
|
|
_, err = ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = model.NewId()
|
|
s2.DeviceId = model.NewId()
|
|
s2.ExpiresAt = now - TenMinutes // expired within threshold
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = model.NewId()
|
|
s3.DeviceId = model.NewId()
|
|
s3.ExpiresAt = now - (TenMinutes * 100) // expired outside threshold
|
|
_, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
s4 := &model.Session{}
|
|
s4.UserId = model.NewId()
|
|
s4.ExpiresAt = now - TenMinutes // expired within threshold, but not mobile
|
|
s4, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
s5 := &model.Session{}
|
|
s5.UserId = model.NewId()
|
|
s5.DeviceId = model.NewId()
|
|
s5.ExpiresAt = now + (TenMinutes * 100000) // not expired
|
|
_, err = ss.Session().Save(rctx, s5)
|
|
require.NoError(t, err)
|
|
|
|
sessions, err := ss.Session().GetSessionsExpired(TenMinutes*2, true, true) // mobile only
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 1)
|
|
require.Equal(t, s2.Id, sessions[0].Id)
|
|
|
|
sessions, err = ss.Session().GetSessionsExpired(TenMinutes*2, false, true) // all client types
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 2)
|
|
expected := []string{s2.Id, s4.Id}
|
|
for _, sess := range sessions {
|
|
require.Contains(t, expected, sess.Id)
|
|
}
|
|
}
|
|
|
|
func testGetSessionsWithActiveDeviceIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userId := model.NewId()
|
|
|
|
// Create session 1 with a device ID
|
|
s1 := &model.Session{}
|
|
s1.UserId = userId
|
|
s1.ExpiresAt = model.GetMillis() + 100000
|
|
s1.DeviceId = model.NewId()
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 2 with a device ID and a prop for last_removed_device_id that doesn't match the device ID
|
|
s2 := &model.Session{}
|
|
s2.UserId = userId
|
|
s2.ExpiresAt = model.GetMillis() + 100000
|
|
s2.DeviceId = model.NewId()
|
|
s2.AddProp(model.SessionPropLastRemovedDeviceId, model.NewId())
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 3 with a device ID and a prop for last_removed_device_id that matches the device ID - this should be filtered out
|
|
s3 := &model.Session{}
|
|
s3.UserId = userId
|
|
s3.ExpiresAt = model.GetMillis() + 100000
|
|
s3.DeviceId = model.NewId()
|
|
s3.AddProp(model.SessionPropLastRemovedDeviceId, s3.DeviceId)
|
|
s3, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 4 with no device ID - this should be filtered out
|
|
s4 := &model.Session{}
|
|
s4.UserId = userId
|
|
s4.ExpiresAt = model.GetMillis() + 100000
|
|
s4, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 5 with a device ID but expired - this should be filtered out
|
|
s5 := &model.Session{}
|
|
s5.UserId = userId
|
|
s5.ExpiresAt = model.GetMillis() - 100000
|
|
s5.DeviceId = model.NewId()
|
|
s5, err = ss.Session().Save(rctx, s5)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 6 with dead standard token but live VoIP token - should be INCLUDED
|
|
// (the "silence chat, keep ringing" case: alert topic dead, PushKit still alive).
|
|
s6 := &model.Session{}
|
|
s6.UserId = userId
|
|
s6.ExpiresAt = model.GetMillis() + 100000
|
|
s6.DeviceId = model.NewId()
|
|
s6.VoIPDeviceId = model.NewId()
|
|
s6.AddProp(model.SessionPropLastRemovedDeviceId, s6.DeviceId)
|
|
s6, err = ss.Session().Save(rctx, s6)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 7 with live standard + dead VoIP - should be INCLUDED (via standard).
|
|
s7 := &model.Session{}
|
|
s7.UserId = userId
|
|
s7.ExpiresAt = model.GetMillis() + 100000
|
|
s7.DeviceId = model.NewId()
|
|
s7.VoIPDeviceId = model.NewId()
|
|
s7.AddProp(model.SessionPropLastRemovedVoIPDeviceId, s7.VoIPDeviceId)
|
|
s7, err = ss.Session().Save(rctx, s7)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 8 with both tokens dead - should be EXCLUDED.
|
|
s8 := &model.Session{}
|
|
s8.UserId = userId
|
|
s8.ExpiresAt = model.GetMillis() + 100000
|
|
s8.DeviceId = model.NewId()
|
|
s8.VoIPDeviceId = model.NewId()
|
|
s8.AddProp(model.SessionPropLastRemovedDeviceId, s8.DeviceId)
|
|
s8.AddProp(model.SessionPropLastRemovedVoIPDeviceId, s8.VoIPDeviceId)
|
|
s8, err = ss.Session().Save(rctx, s8)
|
|
require.NoError(t, err)
|
|
|
|
// Create session 9 with VoIP token only (no standard) - should be INCLUDED.
|
|
s9 := &model.Session{}
|
|
s9.UserId = userId
|
|
s9.ExpiresAt = model.GetMillis() + 100000
|
|
s9.VoIPDeviceId = model.NewId()
|
|
s9, err = ss.Session().Save(rctx, s9)
|
|
require.NoError(t, err)
|
|
|
|
// Get sessions with active device IDs
|
|
sessions, err := ss.Session().GetSessionsWithActiveDeviceIds(userId)
|
|
require.NoError(t, err)
|
|
|
|
// Expected included: s1, s2, s6, s7, s9.
|
|
require.Len(t, sessions, 5)
|
|
|
|
sessionIds := make(map[string]bool)
|
|
for _, session := range sessions {
|
|
sessionIds[session.Id] = true
|
|
}
|
|
require.True(t, sessionIds[s1.Id])
|
|
require.True(t, sessionIds[s2.Id])
|
|
require.True(t, sessionIds[s6.Id])
|
|
require.True(t, sessionIds[s7.Id])
|
|
require.True(t, sessionIds[s9.Id])
|
|
|
|
// Excluded: s3 (dead standard, no VoIP), s4 (no device IDs), s5 (expired),
|
|
// s8 (both dead).
|
|
require.False(t, sessionIds[s3.Id])
|
|
require.False(t, sessionIds[s4.Id])
|
|
require.False(t, sessionIds[s5.Id])
|
|
require.False(t, sessionIds[s8.Id])
|
|
}
|
|
|
|
func testUpdateExpiredNotify(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
s1 := &model.Session{}
|
|
s1.UserId = model.NewId()
|
|
s1.DeviceId = model.NewId()
|
|
s1.ExpiresAt = model.GetMillis() + TenMinutes
|
|
s1, err := ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
session, err := ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.False(t, session.ExpiredNotify)
|
|
|
|
err = ss.Session().UpdateExpiredNotify(session.Id, true)
|
|
require.NoError(t, err)
|
|
session, err = ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.True(t, session.ExpiredNotify)
|
|
|
|
err = ss.Session().UpdateExpiredNotify(session.Id, false)
|
|
require.NoError(t, err)
|
|
session, err = ss.Session().Get(rctx, s1.Id)
|
|
require.NoError(t, err)
|
|
require.False(t, session.ExpiredNotify)
|
|
}
|
|
|
|
func testGetLRUSessions(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userId := model.NewId()
|
|
|
|
// Clear existing sessions.
|
|
err := ss.Session().RemoveAllSessions()
|
|
require.NoError(t, err)
|
|
|
|
s1 := &model.Session{}
|
|
s1.UserId = userId
|
|
s1.DeviceId = model.NewId()
|
|
_, err = ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = userId
|
|
s2.DeviceId = model.NewId()
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = userId
|
|
s3.DeviceId = model.NewId()
|
|
s3, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
sessions, err := ss.Session().GetLRUSessions(rctx, userId, 3, 3)
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 0)
|
|
|
|
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 1)
|
|
require.Equal(t, s1.Id, sessions[0].Id)
|
|
|
|
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 1)
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 2)
|
|
require.Equal(t, s2.Id, sessions[0].Id)
|
|
require.Equal(t, s1.Id, sessions[1].Id)
|
|
|
|
sessions, err = ss.Session().GetLRUSessions(rctx, userId, 3, 0)
|
|
require.NoError(t, err)
|
|
require.Len(t, sessions, 3)
|
|
require.Equal(t, s3.Id, sessions[0].Id)
|
|
require.Equal(t, s2.Id, sessions[1].Id)
|
|
require.Equal(t, s1.Id, sessions[2].Id)
|
|
}
|
|
|
|
func testGetAllSessionsWithActiveDeviceIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
err := ss.Session().RemoveAllSessions()
|
|
require.NoError(t, err)
|
|
|
|
userId1 := model.NewId()
|
|
userId2 := model.NewId()
|
|
|
|
s1 := &model.Session{UserId: userId1, ExpiresAt: model.GetMillis() + 100000, DeviceId: model.NewId()}
|
|
s1, err = ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{UserId: userId2, ExpiresAt: model.GetMillis() + 100000, DeviceId: model.NewId()}
|
|
s2, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
// no device ID — excluded
|
|
s3 := &model.Session{UserId: userId1, ExpiresAt: model.GetMillis() + 100000}
|
|
s3, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
// expired — still included so the wipe reaches devices with stale local data
|
|
s4 := &model.Session{UserId: userId2, ExpiresAt: model.GetMillis() - 100000, DeviceId: model.NewId()}
|
|
s4, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
sessions, err := ss.Session().GetAllSessionsWithActiveDeviceIds()
|
|
require.NoError(t, err)
|
|
|
|
sessionIds := make(map[string]bool, len(sessions))
|
|
for _, s := range sessions {
|
|
sessionIds[s.Id] = true
|
|
}
|
|
require.True(t, sessionIds[s1.Id], "expected session from user1")
|
|
require.True(t, sessionIds[s2.Id], "expected session from user2")
|
|
require.False(t, sessionIds[s3.Id], "expected session without device ID excluded")
|
|
require.True(t, sessionIds[s4.Id], "expected expired session included for wipe")
|
|
}
|
|
|
|
func testGetMobileSessionMetadata(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userId1 := model.NewId()
|
|
userId2 := model.NewId()
|
|
userId3 := model.NewId()
|
|
userId4 := model.NewId()
|
|
userId5 := model.NewId()
|
|
|
|
// Clear existing sessions.
|
|
err := ss.Session().RemoveAllSessions()
|
|
require.NoError(t, err)
|
|
|
|
s1 := &model.Session{}
|
|
s1.UserId = userId1
|
|
s1.ExpiresAt = model.GetMillis() + 10000
|
|
|
|
_, err = ss.Session().Save(rctx, s1)
|
|
require.NoError(t, err)
|
|
|
|
s2 := &model.Session{}
|
|
s2.UserId = userId2
|
|
s2.DeviceId = "android:" + model.NewId()
|
|
s2.ExpiresAt = model.GetMillis() + 10000
|
|
s2.Props = model.StringMap{
|
|
model.SessionPropDeviceNotificationDisabled: "false",
|
|
model.SessionPropMobileVersion: "1.2.3",
|
|
}
|
|
|
|
_, err = ss.Session().Save(rctx, s2)
|
|
require.NoError(t, err)
|
|
|
|
s3 := &model.Session{}
|
|
s3.UserId = userId3
|
|
s3.DeviceId = "ios:" + model.NewId()
|
|
s3.ExpiresAt = model.GetMillis() + 10000
|
|
s3.Props = model.StringMap{
|
|
model.SessionPropDeviceNotificationDisabled: "true",
|
|
model.SessionPropMobileVersion: "1.2.3",
|
|
}
|
|
|
|
_, err = ss.Session().Save(rctx, s3)
|
|
require.NoError(t, err)
|
|
|
|
s4 := &model.Session{}
|
|
s4.UserId = userId4
|
|
s4.DeviceId = "android:" + model.NewId()
|
|
s4.ExpiresAt = model.GetMillis() + 10000
|
|
s4.Props = model.StringMap{
|
|
model.SessionPropDeviceNotificationDisabled: "true",
|
|
model.SessionPropMobileVersion: "3.2.1",
|
|
}
|
|
|
|
_, err = ss.Session().Save(rctx, s4)
|
|
require.NoError(t, err)
|
|
|
|
s5 := &model.Session{}
|
|
s5.UserId = userId5
|
|
s5.DeviceId = "android:" + model.NewId()
|
|
s5.ExpiresAt = model.GetMillis() + 10000
|
|
s5.Props = model.StringMap{
|
|
model.SessionPropDeviceNotificationDisabled: "true",
|
|
model.SessionPropMobileVersion: "3.2.1",
|
|
}
|
|
|
|
_, err = ss.Session().Save(rctx, s5)
|
|
require.NoError(t, err)
|
|
|
|
metadata, err := ss.Session().GetMobileSessionMetadata()
|
|
require.NoError(t, err)
|
|
require.Len(t, metadata, 4)
|
|
found := false
|
|
for _, d := range metadata {
|
|
if d.NotificationDisabled == "true" &&
|
|
d.Platform == "android" &&
|
|
d.Version == "3.2.1" {
|
|
found = true
|
|
require.Equal(t, float64(2), d.Count)
|
|
}
|
|
}
|
|
require.True(t, found)
|
|
}
|