PLT-7633 (E20) Add session idle timeout config setting (#7524)

* Add session idle timeout config setting

* Modify config setting name to SessionIdleTimeoutInMinutes

* Small re-org of if statement

* Merge with latest master
This commit is contained in:
Joram Wilander
2017-09-28 09:04:52 -04:00
committed by George Goldberg
parent 8d662105d3
commit 15b361094a
5 changed files with 139 additions and 0 deletions

View File

@@ -157,6 +157,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized)
} else {
c.Session = *session
c.App.UpdateLastActivityAtIfNeeded(*session)
}
}

View File

@@ -71,6 +71,18 @@ func (a *App) GetSession(token string) (*model.Session, *model.AppError) {
return nil, model.NewAppError("GetSession", "api.context.invalid_token.error", map[string]interface{}{"Token": token}, "", http.StatusUnauthorized)
}
if *utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes > 0 &&
utils.IsLicensed() && *utils.License().Features.Compliance &&
session != nil && !session.IsOAuth && !session.IsMobileApp() &&
session.Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_USER_ACCESS_TOKEN {
timeout := int64(*utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes) * 1000 * 60
if model.GetMillis()-session.LastActivityAt > timeout {
a.RevokeSessionById(session.Id)
return nil, model.NewAppError("GetSession", "api.context.invalid_token.error", map[string]interface{}{"Token": token}, "idle timeout", http.StatusUnauthorized)
}
}
return session, nil
}

View File

@@ -7,6 +7,10 @@ import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCache(t *testing.T) {
@@ -32,3 +36,118 @@ func TestCache(t *testing.T) {
t.Fatal("should have one less")
}
}
func TestGetSessionIdleTimeoutInMinutes(t *testing.T) {
th := Setup().InitBasic()
session := &model.Session{
UserId: model.NewId(),
}
session, _ = th.App.CreateSession(session)
isLicensed := utils.IsLicensed()
license := utils.License()
timeout := *utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes
defer func() {
utils.SetIsLicensed(isLicensed)
utils.SetLicense(license)
*utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes = timeout
}()
utils.SetIsLicensed(true)
utils.SetLicense(&model.License{Features: &model.Features{}})
utils.License().Features.SetDefaults()
*utils.License().Features.Compliance = true
*utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 5
rsession, err := th.App.GetSession(session.Token)
require.Nil(t, err)
assert.Equal(t, rsession.Id, session.Id)
rsession, err = th.App.GetSession(session.Token)
// Test regular session, should timeout
time := session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
rsession, err = th.App.GetSession(session.Token)
require.NotNil(t, err)
assert.Equal(t, "api.context.invalid_token.error", err.Id)
assert.Equal(t, "idle timeout", err.DetailedError)
assert.Nil(t, rsession)
// Test mobile session, should not timeout
session = &model.Session{
UserId: model.NewId(),
DeviceId: "android:" + model.NewId(),
}
session, _ = th.App.CreateSession(session)
time = session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
_, err = th.App.GetSession(session.Token)
assert.Nil(t, err)
// Test oauth session, should not timeout
session = &model.Session{
UserId: model.NewId(),
IsOAuth: true,
}
session, _ = th.App.CreateSession(session)
time = session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
_, err = th.App.GetSession(session.Token)
assert.Nil(t, err)
// Test personal access token session, should not timeout
session = &model.Session{
UserId: model.NewId(),
}
session.AddProp(model.SESSION_PROP_TYPE, model.SESSION_TYPE_USER_ACCESS_TOKEN)
session, _ = th.App.CreateSession(session)
time = session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
_, err = th.App.GetSession(session.Token)
assert.Nil(t, err)
// Test regular session with license off, should not timeout
*utils.License().Features.Compliance = false
session = &model.Session{
UserId: model.NewId(),
}
session, _ = th.App.CreateSession(session)
time = session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
_, err = th.App.GetSession(session.Token)
assert.Nil(t, err)
*utils.License().Features.Compliance = true
// Test regular session with timeout set to 0, should not timeout
*utils.Cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 0
session = &model.Session{
UserId: model.NewId(),
}
session, _ = th.App.CreateSession(session)
time = session.LastActivityAt - (1000 * 60 * 6)
<-th.App.Srv.Store.Session().UpdateLastActivityAt(session.Id, time)
th.App.ClearSessionCacheForUserSkipClusterSend(session.UserId)
_, err = th.App.GetSession(session.Token)
assert.Nil(t, err)
}

View File

@@ -36,6 +36,7 @@
"SessionLengthMobileInDays": 30,
"SessionLengthSSOInDays": 30,
"SessionCacheInMinutes": 10,
"SessionIdleTimeout": 0,
"WebsocketSecurePort": 443,
"WebsocketPort": 80,
"WebserverMode": "gzip",

View File

@@ -182,6 +182,7 @@ type ServiceSettings struct {
SessionLengthMobileInDays *int
SessionLengthSSOInDays *int
SessionCacheInMinutes *int
SessionIdleTimeoutInMinutes *int
WebsocketSecurePort *int
WebsocketPort *int
WebserverMode *string
@@ -1155,6 +1156,11 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.SessionCacheInMinutes = 10
}
if o.ServiceSettings.SessionIdleTimeoutInMinutes == nil {
o.ServiceSettings.SessionIdleTimeoutInMinutes = new(int)
*o.ServiceSettings.SessionIdleTimeoutInMinutes = 0
}
if o.ServiceSettings.EnableCommands == nil {
o.ServiceSettings.EnableCommands = new(bool)
*o.ServiceSettings.EnableCommands = false