diff options
-rw-r--r-- | api/context.go | 9 | ||||
-rw-r--r-- | api/user.go | 8 | ||||
-rw-r--r-- | config/config.json | 6 | ||||
-rw-r--r-- | docker/dev/config_docker.json | 6 | ||||
-rw-r--r-- | docker/local/config_docker.json | 6 | ||||
-rw-r--r-- | manualtesting/manual_testing.go | 2 | ||||
-rw-r--r-- | model/config.go | 24 | ||||
-rw-r--r-- | model/session.go | 21 | ||||
-rw-r--r-- | web/react/components/admin_console/service_settings.jsx | 133 | ||||
-rw-r--r-- | web/web.go | 2 |
10 files changed, 192 insertions, 25 deletions
diff --git a/api/context.go b/api/context.go index a6f9bc1e1..b39f03a7d 100644 --- a/api/context.go +++ b/api/context.go @@ -523,6 +523,13 @@ func GetSession(token string) *model.Session { l4g.Error("Invalid session token=" + token + ", err=" + sessionResult.Err.DetailedError) } else { session = sessionResult.Data.(*model.Session) + + if session.IsExpired() { + return nil + } else { + AddSessionToCache(session) + return session + } } } @@ -553,5 +560,5 @@ func FindMultiSessionForTeamId(r *http.Request, teamId string) (int64, *model.Se } func AddSessionToCache(session *model.Session) { - sessionCache.Add(session.Token, session) + sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*utils.Cfg.ServiceSettings.SessionCacheInMinutes*60)) } diff --git a/api/user.go b/api/user.go index 42a65c934..d4c7fcaf5 100644 --- a/api/user.go +++ b/api/user.go @@ -492,11 +492,11 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false} - maxAge := model.SESSION_TIME_WEB_IN_SECS + maxAge := *utils.Cfg.ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24 if len(deviceId) > 0 { - session.SetExpireInDays(model.SESSION_TIME_MOBILE_IN_DAYS) - maxAge = model.SESSION_TIME_MOBILE_IN_SECS + session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthMobileInDays) + maxAge = *utils.Cfg.ServiceSettings.SessionLengthMobileInDays * 60 * 60 * 24 // A special case where we logout of all other sessions with the same Id if result := <-Srv.Store.Session().GetSessions(user.Id); result.Err != nil { @@ -518,7 +518,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, } } else { - session.SetExpireInDays(model.SESSION_TIME_WEB_IN_DAYS) + session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthWebInDays) } ua := user_agent.New(r.UserAgent()) diff --git a/config/config.json b/config/config.json index 7c2ec9426..c43db1e50 100644 --- a/config/config.json +++ b/config/config.json @@ -11,7 +11,11 @@ "EnablePostIconOverride": false, "EnableTesting": false, "EnableDeveloper": false, - "EnableSecurityFixAlert": true + "EnableSecurityFixAlert": true, + "SessionLengthWebInDays" : 30, + "SessionLengthMobileInDays" : 30, + "SessionLengthSSOInDays" : 30, + "SessionCacheInMinutes" : 10 }, "TeamSettings": { "SiteName": "Mattermost", diff --git a/docker/dev/config_docker.json b/docker/dev/config_docker.json index 3a5195de3..1aa2ee843 100644 --- a/docker/dev/config_docker.json +++ b/docker/dev/config_docker.json @@ -11,7 +11,11 @@ "EnablePostIconOverride": false, "EnableTesting": false, "EnableDeveloper": false, - "EnableSecurityFixAlert": true + "EnableSecurityFixAlert": true, + "SessionLengthWebInDays" : 30, + "SessionLengthMobileInDays" : 30, + "SessionLengthSSOInDays" : 30, + "SessionCacheInMinutes" : 10 }, "TeamSettings": { "SiteName": "Mattermost", diff --git a/docker/local/config_docker.json b/docker/local/config_docker.json index 3a5195de3..1aa2ee843 100644 --- a/docker/local/config_docker.json +++ b/docker/local/config_docker.json @@ -11,7 +11,11 @@ "EnablePostIconOverride": false, "EnableTesting": false, "EnableDeveloper": false, - "EnableSecurityFixAlert": true + "EnableSecurityFixAlert": true, + "SessionLengthWebInDays" : 30, + "SessionLengthMobileInDays" : 30, + "SessionLengthSSOInDays" : 30, + "SessionCacheInMinutes" : 10 }, "TeamSettings": { "SiteName": "Mattermost", diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index ffdb578a4..b3d01a5a6 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -114,7 +114,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { Name: model.SESSION_COOKIE_TOKEN, Value: client.AuthToken, Path: "/", - MaxAge: model.SESSION_TIME_WEB_IN_SECS, + MaxAge: *utils.Cfg.ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24, HttpOnly: true, } http.SetCookie(w, sessionCookie) diff --git a/model/config.go b/model/config.go index a4792ff9e..ed56ed0c7 100644 --- a/model/config.go +++ b/model/config.go @@ -36,6 +36,10 @@ type ServiceSettings struct { EnableTesting bool EnableDeveloper *bool EnableSecurityFixAlert *bool + SessionLengthWebInDays *int + SessionLengthMobileInDays *int + SessionLengthSSOInDays *int + SessionCacheInMinutes *int } type SSOSettings struct { @@ -306,6 +310,26 @@ func (o *Config) SetDefaults() { o.LdapSettings.Enable = new(bool) *o.LdapSettings.Enable = false } + + if o.ServiceSettings.SessionLengthWebInDays == nil { + o.ServiceSettings.SessionLengthWebInDays = new(int) + *o.ServiceSettings.SessionLengthWebInDays = 30 + } + + if o.ServiceSettings.SessionLengthMobileInDays == nil { + o.ServiceSettings.SessionLengthMobileInDays = new(int) + *o.ServiceSettings.SessionLengthMobileInDays = 30 + } + + if o.ServiceSettings.SessionLengthSSOInDays == nil { + o.ServiceSettings.SessionLengthSSOInDays = new(int) + *o.ServiceSettings.SessionLengthSSOInDays = 30 + } + + if o.ServiceSettings.SessionCacheInMinutes == nil { + o.ServiceSettings.SessionCacheInMinutes = new(int) + *o.ServiceSettings.SessionCacheInMinutes = 10 + } } func (o *Config) IsValid() *AppError { diff --git a/model/session.go b/model/session.go index 5fe74a161..5d9424d64 100644 --- a/model/session.go +++ b/model/session.go @@ -9,18 +9,11 @@ import ( ) const ( - SESSION_COOKIE_TOKEN = "MMTOKEN" - SESSION_TIME_WEB_IN_DAYS = 30 - SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS - SESSION_TIME_MOBILE_IN_DAYS = 30 - SESSION_TIME_MOBILE_IN_SECS = 60 * 60 * 24 * SESSION_TIME_MOBILE_IN_DAYS - SESSION_TIME_OAUTH_IN_DAYS = 365 - SESSION_TIME_OAUTH_IN_SECS = 60 * 60 * 24 * SESSION_TIME_OAUTH_IN_DAYS - SESSION_CACHE_IN_SECS = 60 * 10 - SESSION_CACHE_SIZE = 10000 - SESSION_PROP_PLATFORM = "platform" - SESSION_PROP_OS = "os" - SESSION_PROP_BROWSER = "browser" + SESSION_COOKIE_TOKEN = "MMTOKEN" + SESSION_CACHE_SIZE = 10000 + SESSION_PROP_PLATFORM = "platform" + SESSION_PROP_OS = "os" + SESSION_PROP_BROWSER = "browser" ) type Session struct { @@ -89,8 +82,8 @@ func (me *Session) IsExpired() bool { return false } -func (me *Session) SetExpireInDays(days int64) { - me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * days) +func (me *Session) SetExpireInDays(days int) { + me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days)) } func (me *Session) AddProp(key string, value string) { diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx index e235819fe..f10721ffa 100644 --- a/web/react/components/admin_console/service_settings.jsx +++ b/web/react/components/admin_console/service_settings.jsx @@ -4,6 +4,10 @@ import * as Client from '../../utils/client.jsx'; import * as AsyncClient from '../../utils/async_client.jsx'; +const DefaultSessionLength = 30; +const DefaultMaximumLoginAttempts = 10; +const DefaultSessionCacheInMinutes = 10; + export default class ServiceSettings extends React.Component { constructor(props) { super(props); @@ -45,13 +49,56 @@ export default class ServiceSettings extends React.Component { //config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked; - var MaximumLoginAttempts = 10; + var MaximumLoginAttempts = DefaultMaximumLoginAttempts; if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) { MaximumLoginAttempts = parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10); } + if (MaximumLoginAttempts < 1) { + MaximumLoginAttempts = 1; + } config.ServiceSettings.MaximumLoginAttempts = MaximumLoginAttempts; ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value = MaximumLoginAttempts; + var SessionLengthWebInDays = DefaultSessionLength; + if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10))) { + SessionLengthWebInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10); + } + if (SessionLengthWebInDays < 1) { + SessionLengthWebInDays = 1; + } + config.ServiceSettings.SessionLengthWebInDays = SessionLengthWebInDays; + ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value = SessionLengthWebInDays; + + var SessionLengthMobileInDays = DefaultSessionLength; + if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10))) { + SessionLengthMobileInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10); + } + if (SessionLengthMobileInDays < 1) { + SessionLengthMobileInDays = 1; + } + config.ServiceSettings.SessionLengthMobileInDays = SessionLengthMobileInDays; + ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value = SessionLengthMobileInDays; + + var SessionLengthSSOInDays = DefaultSessionLength; + if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10))) { + SessionLengthSSOInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10); + } + if (SessionLengthSSOInDays < 1) { + SessionLengthSSOInDays = 1; + } + config.ServiceSettings.SessionLengthSSOInDays = SessionLengthSSOInDays; + ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value = SessionLengthSSOInDays; + + var SessionCacheInMinutes = DefaultSessionCacheInMinutes; + if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10))) { + SessionCacheInMinutes = parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10); + } + if (SessionCacheInMinutes < -1) { + SessionCacheInMinutes = -1; + } + config.ServiceSettings.SessionCacheInMinutes = SessionCacheInMinutes; + ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value = SessionCacheInMinutes; + Client.saveConfig( config, () => { @@ -417,6 +464,90 @@ export default class ServiceSettings extends React.Component { </div> <div className='form-group'> + <label + className='control-label col-sm-4' + htmlFor='SessionLengthWebInDays' + > + {'Session Length for Web in Days:'} + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='SessionLengthWebInDays' + ref='SessionLengthWebInDays' + placeholder='Ex "30"' + defaultValue={this.props.config.ServiceSettings.SessionLengthWebInDays} + onChange={this.handleChange} + /> + <p className='help-text'>{'The web session will expire after the number of days specified and will require a user to login again.'}</p> + </div> + </div> + + <div className='form-group'> + <label + className='control-label col-sm-4' + htmlFor='SessionLengthMobileInDays' + > + {'Session Length for Mobile Device in Days:'} + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='SessionLengthMobileInDays' + ref='SessionLengthMobileInDays' + placeholder='Ex "30"' + defaultValue={this.props.config.ServiceSettings.SessionLengthMobileInDays} + onChange={this.handleChange} + /> + <p className='help-text'>{'The native mobile session will expire after the number of days specified and will require a user to login again.'}</p> + </div> + </div> + + <div className='form-group'> + <label + className='control-label col-sm-4' + htmlFor='SessionLengthSSOInDays' + > + {'Session Length for SSO in Days:'} + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='SessionLengthSSOInDays' + ref='SessionLengthSSOInDays' + placeholder='Ex "30"' + defaultValue={this.props.config.ServiceSettings.SessionLengthSSOInDays} + onChange={this.handleChange} + /> + <p className='help-text'>{'The SSO session will expire after the number of days specified and will require a user to login again.'}</p> + </div> + </div> + + <div className='form-group'> + <label + className='control-label col-sm-4' + htmlFor='SessionCacheInMinutes' + > + {'Session Cache in Minutes:'} + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='SessionCacheInMinutes' + ref='SessionCacheInMinutes' + placeholder='Ex "30"' + defaultValue={this.props.config.ServiceSettings.SessionCacheInMinutes} + onChange={this.handleChange} + /> + <p className='help-text'>{'The number of minutes to cache a session in memory.'}</p> + </div> + </div> + + <div className='form-group'> <div className='col-sm-12'> {serverError} <button diff --git a/web/web.go b/web/web.go index 30a70ba2e..bf1208adc 100644 --- a/web/web.go +++ b/web/web.go @@ -998,7 +998,7 @@ func getAccessToken(c *api.Context, w http.ResponseWriter, r *http.Request) { return } - accessRsp := &model.AccessResponse{AccessToken: session.Token, TokenType: model.ACCESS_TOKEN_TYPE, ExpiresIn: model.SESSION_TIME_OAUTH_IN_SECS} + accessRsp := &model.AccessResponse{AccessToken: session.Token, TokenType: model.ACCESS_TOKEN_TYPE, ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24)} w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") |