[MM-14081] Disable checkMFA Endpoint by default and add tests for MFA login (#10356)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
6a3fdbd489
Коммит
dcf611b735
@@ -915,7 +915,15 @@ func updateUserAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(user.ToJson()))
|
w.Write([]byte(user.ToJson()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: checkUserMfa is deprecated and should not be used anymore, starting with version 6.0 it will be disabled.
|
||||||
|
// Clients should attempt a login without MFA and will receive a MFA error when it's required.
|
||||||
func checkUserMfa(c *Context, w http.ResponseWriter, r *http.Request) {
|
func checkUserMfa(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
if *c.App.Config().ServiceSettings.DisableLegacyMFA {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
props := model.MapFromJson(r.Body)
|
props := model.MapFromJson(r.Body)
|
||||||
|
|
||||||
loginId := props["login_id"]
|
loginId := props["login_id"]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package api4
|
package api4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/dgryski/dgoogauth"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1804,6 +1805,7 @@ func TestUpdateUserMfa(t *testing.T) {
|
|||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckUserMfa is deprecated and should not be used anymore, it will be disabled by default in version 6.0
|
||||||
func TestCheckUserMfa(t *testing.T) {
|
func TestCheckUserMfa(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -1847,6 +1849,68 @@ func TestCheckUserMfa(t *testing.T) {
|
|||||||
if required {
|
if required {
|
||||||
t.Fatal("should be false - mfa not active")
|
t.Fatal("should be false - mfa not active")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func (c *model.Config){
|
||||||
|
*c.ServiceSettings.DisableLegacyMFA = true
|
||||||
|
})
|
||||||
|
|
||||||
|
_, resp = th.Client.CheckUserMfa(th.BasicUser.Email)
|
||||||
|
CheckNotFoundStatus(t, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserLoginMFAFlow(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func (c *model.Config){
|
||||||
|
*c.ServiceSettings.DisableLegacyMFA = true
|
||||||
|
*c.ServiceSettings.EnableMultifactorAuthentication = true
|
||||||
|
})
|
||||||
|
|
||||||
|
secret, err := th.App.GenerateMfaSecret(th.BasicUser.Id)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
t.Run("WithoutMFA", func (t *testing.T){
|
||||||
|
_, resp := th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fake user has MFA enabled
|
||||||
|
if result := <-th.Server.Store.User().UpdateMfaActive(th.BasicUser.Id, true); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-th.Server.Store.User().UpdateMfaSecret(th.BasicUser.Id, secret.Secret); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("WithInvalidMFA", func (t *testing.T){
|
||||||
|
user, resp := th.Client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||||
|
CheckErrorMessage(t, resp, "mfa.validate_token.authenticate.app_error")
|
||||||
|
assert.Nil(t, user)
|
||||||
|
|
||||||
|
user, resp = th.Client.LoginWithMFA(th.BasicUser.Email, th.BasicUser.Password, "")
|
||||||
|
CheckErrorMessage(t, resp, "mfa.validate_token.authenticate.app_error")
|
||||||
|
assert.Nil(t, user)
|
||||||
|
|
||||||
|
user, resp = th.Client.LoginWithMFA(th.BasicUser.Email, th.BasicUser.Password, "abcdefgh")
|
||||||
|
CheckErrorMessage(t, resp, "mfa.validate_token.authenticate.app_error")
|
||||||
|
assert.Nil(t, user)
|
||||||
|
|
||||||
|
secret2, err := th.App.GenerateMfaSecret(th.BasicUser2.Id)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
user, resp = th.Client.LoginWithMFA(th.BasicUser.Email, th.BasicUser.Password, secret2.Secret)
|
||||||
|
CheckErrorMessage(t, resp, "mfa.validate_token.authenticate.app_error")
|
||||||
|
assert.Nil(t, user)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("WithCorrectMFA", func (t *testing.T){
|
||||||
|
code := dgoogauth.ComputeCode(secret.Secret, time.Now().UTC().Unix() / 30)
|
||||||
|
|
||||||
|
user, resp := th.Client.LoginWithMFA(th.BasicUser.Email, th.BasicUser.Password, strconv.Itoa(code))
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateMfaSecret(t *testing.T) {
|
func TestGenerateMfaSecret(t *testing.T) {
|
||||||
|
|||||||
@@ -53,8 +53,12 @@ func (a *App) CheckPasswordAndAllCriteria(user *model.User, password string, mfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := a.CheckUserMfa(user, mfaToken); err != nil {
|
if err := a.CheckUserMfa(user, mfaToken); err != nil {
|
||||||
if result := <-a.Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil {
|
// If the mfaToken is not set, we assume the client used this as a pre-flight request to query the server
|
||||||
return result.Err
|
// about the MFA state of the user in question
|
||||||
|
if mfaToken != "" {
|
||||||
|
if result := <-a.Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"ExperimentalChannelOrganization": false,
|
"ExperimentalChannelOrganization": false,
|
||||||
"EnableAPITeamDeletion": false,
|
"EnableAPITeamDeletion": false,
|
||||||
"ExperimentalEnableHardenedMode": false,
|
"ExperimentalEnableHardenedMode": false,
|
||||||
|
"DisableLegacyMFA": true,
|
||||||
"EnableEmailInvitations": false,
|
"EnableEmailInvitations": false,
|
||||||
"ExperimentalLdapGroupSync": false,
|
"ExperimentalLdapGroupSync": false,
|
||||||
"ExperimentalStrictCSRFEnforcement": false
|
"ExperimentalStrictCSRFEnforcement": false
|
||||||
|
|||||||
@@ -991,6 +991,7 @@ func (c *Client4) UpdateUserMfa(userId, code string, activate bool) (bool, *Resp
|
|||||||
|
|
||||||
// CheckUserMfa checks whether a user has MFA active on their account or not based on the
|
// CheckUserMfa checks whether a user has MFA active on their account or not based on the
|
||||||
// provided login id.
|
// provided login id.
|
||||||
|
// Deprecated: Clients should use Login method and check for MFA Error
|
||||||
func (c *Client4) CheckUserMfa(loginId string) (bool, *Response) {
|
func (c *Client4) CheckUserMfa(loginId string) (bool, *Response) {
|
||||||
requestBody := make(map[string]interface{})
|
requestBody := make(map[string]interface{})
|
||||||
requestBody["login_id"] = loginId
|
requestBody["login_id"] = loginId
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ type ServiceSettings struct {
|
|||||||
DEPRECATED_DO_NOT_USE_ImageProxyOptions *string `json:"ImageProxyOptions" mapstructure:"ImageProxyOptions"` // This field is deprecated and must not be used.
|
DEPRECATED_DO_NOT_USE_ImageProxyOptions *string `json:"ImageProxyOptions" mapstructure:"ImageProxyOptions"` // This field is deprecated and must not be used.
|
||||||
EnableAPITeamDeletion *bool
|
EnableAPITeamDeletion *bool
|
||||||
ExperimentalEnableHardenedMode *bool
|
ExperimentalEnableHardenedMode *bool
|
||||||
|
DisableLegacyMFA *bool
|
||||||
ExperimentalStrictCSRFEnforcement *bool
|
ExperimentalStrictCSRFEnforcement *bool
|
||||||
EnableEmailInvitations *bool
|
EnableEmailInvitations *bool
|
||||||
ExperimentalLdapGroupSync *bool
|
ExperimentalLdapGroupSync *bool
|
||||||
@@ -609,6 +610,10 @@ func (s *ServiceSettings) SetDefaults() {
|
|||||||
s.ExperimentalEnableHardenedMode = NewBool(false)
|
s.ExperimentalEnableHardenedMode = NewBool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.DisableLegacyMFA == nil {
|
||||||
|
s.DisableLegacyMFA = NewBool(false)
|
||||||
|
}
|
||||||
|
|
||||||
if s.ExperimentalLdapGroupSync == nil {
|
if s.ExperimentalLdapGroupSync == nil {
|
||||||
s.ExperimentalLdapGroupSync = NewBool(false)
|
s.ExperimentalLdapGroupSync = NewBool(false)
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user