[MM-58560] Configurable session revocation during password resets (#27286)

* [MM-58560] Allow for configurable session revocation during password reset

* Missing i18n additions

* Update Settings Wording

* Update Settings Wording #2

* Update default_config.ts for Session Termination

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Daniel Schalla
2024-06-18 21:13:29 +02:00
коммит произвёл GitHub
родитель cbd5d95bbb
Коммит 1bbc3b4e83
8 изменённых файлов: 197 добавлений и 0 удалений

Просмотреть файл

@@ -1453,6 +1453,31 @@ func (a *App) UpdatePassword(rctx request.CTX, user *model.User, newPassword str
a.InvalidateCacheForUser(user.Id)
if *a.Config().ServiceSettings.TerminateSessionsOnPasswordChange {
// Get currently active sessions if request is user-initiated to retain it
currentSession := ""
if rctx.Session() != nil && rctx.Session().UserId == user.Id {
currentSession = rctx.Session().Id
}
sessions, err := a.GetSessions(rctx, user.Id)
if err != nil {
return model.NewAppError("UpdatePassword", "api.user.update_password.failed.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
// Revoke all but current session
for _, session := range sessions {
if session.Id == currentSession {
continue
}
err := a.RevokeSessionById(rctx, session.Id)
if err != nil {
return model.NewAppError("UpdatePassword", "api.user.update_password.failed.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
}
return nil
}

Просмотреть файл

@@ -1210,6 +1210,153 @@ func TestInvalidatePasswordRecoveryTokens(t *testing.T) {
})
}
func TestPasswordChangeSessionTermination(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("user-initiated password change with termination enabled", func(t *testing.T) {
th.App.UpdateConfig(func(c *model.Config) {
*c.ServiceSettings.TerminateSessionsOnPasswordChange = true
})
session, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
session2, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
th.Context.Session().UserId = th.BasicUser2.Id
th.Context.Session().Id = session.Id
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password2")
require.Nil(t, err)
session, err = th.App.GetSession(session.Token)
require.Nil(t, err)
require.False(t, session.IsExpired())
session2, err = th.App.GetSession(session2.Token)
require.Error(t, err)
require.Nil(t, session2)
// Cleanup
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password1")
require.Nil(t, err)
th.Context.Session().UserId = ""
th.Context.Session().Id = ""
})
t.Run("user-initiated password change with termination disabled", func(t *testing.T) {
th.App.UpdateConfig(func(c *model.Config) {
*c.ServiceSettings.TerminateSessionsOnPasswordChange = false
})
session, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
session2, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
th.Context.Session().UserId = th.BasicUser2.Id
th.Context.Session().Id = session.Id
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password2")
require.Nil(t, err)
session, err = th.App.GetSession(session.Token)
require.Nil(t, err)
require.False(t, session.IsExpired())
session2, err = th.App.GetSession(session2.Token)
require.Nil(t, err)
require.False(t, session2.IsExpired())
// Cleanup
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password1")
require.Nil(t, err)
th.Context.Session().UserId = ""
th.Context.Session().Id = ""
})
t.Run("admin-initiated password change with termination enabled", func(t *testing.T) {
th.App.UpdateConfig(func(c *model.Config) {
*c.ServiceSettings.TerminateSessionsOnPasswordChange = true
})
session, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
session2, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password2")
require.Nil(t, err)
session, err = th.App.GetSession(session.Token)
require.Error(t, err)
require.Nil(t, session)
session2, err = th.App.GetSession(session2.Token)
require.Error(t, err)
require.Nil(t, session2)
// Cleanup
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password1")
require.Nil(t, err)
})
t.Run("admin-initiated password change with termination disabled", func(t *testing.T) {
th.App.UpdateConfig(func(c *model.Config) {
*c.ServiceSettings.TerminateSessionsOnPasswordChange = false
})
session, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
session2, err := th.App.CreateSession(th.Context, &model.Session{
UserId: th.BasicUser2.Id,
Roles: model.SystemUserRoleId,
})
require.Nil(t, err)
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password2")
require.Nil(t, err)
session, err = th.App.GetSession(session.Token)
require.Nil(t, err)
require.False(t, session.IsExpired())
session2, err = th.App.GetSession(session2.Token)
require.Nil(t, err)
require.False(t, session2.IsExpired())
// Cleanup
err = th.App.UpdatePassword(th.Context, th.BasicUser2, "Password1")
require.Nil(t, err)
})
}
func TestGetViewUsersRestrictions(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

Просмотреть файл

@@ -430,6 +430,7 @@ func (ts *TelemetryService) trackConfig() {
"forward_80_to_443": *cfg.ServiceSettings.Forward80To443,
"maximum_login_attempts": *cfg.ServiceSettings.MaximumLoginAttempts,
"extend_session_length_with_activity": *cfg.ServiceSettings.ExtendSessionLengthWithActivity,
"terminate_sessions_on_password_change": *cfg.ServiceSettings.TerminateSessionsOnPasswordChange,
"session_length_web_in_hours": *cfg.ServiceSettings.SessionLengthWebInHours,
"session_length_mobile_in_hours": *cfg.ServiceSettings.SessionLengthMobileInHours,
"session_length_sso_in_hours": *cfg.ServiceSettings.SessionLengthSSOInHours,

Просмотреть файл

@@ -341,6 +341,7 @@ type ServiceSettings struct {
CorsDebug *bool `access:"integrations_cors,write_restrictable,cloud_restrictable"`
AllowCookiesForSubdomains *bool `access:"write_restrictable,cloud_restrictable"`
ExtendSessionLengthWithActivity *bool `access:"environment_session_lengths,write_restrictable,cloud_restrictable"`
TerminateSessionsOnPasswordChange *bool `access:"environment_session_lengths,write_restrictable,cloud_restrictable"`
// Deprecated
SessionLengthWebInDays *int `access:"environment_session_lengths,write_restrictable,cloud_restrictable"` // telemetry: none
@@ -631,6 +632,11 @@ func (s *ServiceSettings) SetDefaults(isUpdate bool) {
s.ExtendSessionLengthWithActivity = NewBool(!isUpdate)
}
// Must be manually enabled for existing installations.
if s.TerminateSessionsOnPasswordChange == nil {
s.TerminateSessionsOnPasswordChange = NewBool(!isUpdate)
}
if s.SessionLengthWebInDays == nil {
if isUpdate {
s.SessionLengthWebInDays = NewInt(180)