mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
271 lines
9.5 KiB
Go
271 lines
9.5 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package mfa
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dgryski/dgoogauth"
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
"github.com/mattermost/mattermost-server/v5/plugin/plugintest/mock"
|
|
"github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
|
|
"github.com/mattermost/mattermost-server/v5/utils/testutils"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGenerateSecret(t *testing.T) {
|
|
user := &model.User{Id: model.NewId(), Roles: "system_user"}
|
|
|
|
config := model.Config{}
|
|
config.SetDefaults()
|
|
config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true)
|
|
configService := testutils.StaticConfigService{Cfg: &config}
|
|
|
|
t.Run("fail on disabled mfa", func(t *testing.T) {
|
|
wrongConfig := model.Config{}
|
|
wrongConfig.SetDefaults()
|
|
wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false)
|
|
wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig}
|
|
mfa := New(wrongConfigService, nil)
|
|
_, _, err := mfa.GenerateSecret(user)
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.mfa_disabled.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on store action fail", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaSecret", user.Id, mock.AnythingOfType("string")).Return(func(userId string, secret string) *model.AppError {
|
|
return model.NewAppError("GenerateQrCode", "mfa.generate_qr_code.save_secret.app_error", nil, "", http.StatusInternalServerError)
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
|
|
mfa := New(configService, &storeMock)
|
|
_, _, err := mfa.GenerateSecret(user)
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.generate_qr_code.save_secret.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("Successful generate secret", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaSecret", user.Id, mock.AnythingOfType("string")).Return(func(userId string, secret string) *model.AppError {
|
|
return nil
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
|
|
mfa := New(configService, &storeMock)
|
|
|
|
secret, img, err := mfa.GenerateSecret(user)
|
|
require.Nil(t, err)
|
|
assert.Len(t, secret, 32)
|
|
require.NotEmpty(t, img, "no image set")
|
|
})
|
|
}
|
|
|
|
func TestGetIssuerFromUrl(t *testing.T) {
|
|
cases := []struct {
|
|
Input string
|
|
Expected string
|
|
}{
|
|
{"http://somewebsite.com", url.QueryEscape("somewebsite.com")},
|
|
{"https://somewebsite.com", url.QueryEscape("somewebsite.com")},
|
|
{"https://some.website.com", url.QueryEscape("some.website.com")},
|
|
{" https://www.somewebsite.com", url.QueryEscape("somewebsite.com")},
|
|
{"http://somewebsite.com/chat", url.QueryEscape("somewebsite.com/chat")},
|
|
{"somewebsite.com ", url.QueryEscape("somewebsite.com")},
|
|
{"http://localhost:8065", url.QueryEscape("localhost:8065")},
|
|
{"", "Mattermost"},
|
|
{" ", "Mattermost"},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
assert.Equal(t, c.Expected, getIssuerFromUrl(c.Input))
|
|
}
|
|
}
|
|
|
|
func TestActivate(t *testing.T) {
|
|
user := &model.User{Id: model.NewId(), Roles: "system_user"}
|
|
user.MfaSecret = model.NewRandomBase32String(MFA_SECRET_SIZE)
|
|
|
|
token := dgoogauth.ComputeCode(user.MfaSecret, time.Now().UTC().Unix()/30)
|
|
|
|
config := model.Config{}
|
|
config.SetDefaults()
|
|
config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true)
|
|
configService := testutils.StaticConfigService{Cfg: &config}
|
|
|
|
t.Run("fail on disabled mfa", func(t *testing.T) {
|
|
wrongConfig := model.Config{}
|
|
wrongConfig.SetDefaults()
|
|
wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false)
|
|
wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig}
|
|
mfa := New(wrongConfigService, nil)
|
|
err := mfa.Activate(user, "not-important")
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.mfa_disabled.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on wrongly formatted token", func(t *testing.T) {
|
|
mfa := New(configService, nil)
|
|
err := mfa.Activate(user, "invalid-token")
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.activate.authenticate.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on invalid token", func(t *testing.T) {
|
|
mfa := New(configService, nil)
|
|
err := mfa.Activate(user, "000000")
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.activate.bad_token.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on store action fail", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaActive", user.Id, true).Return(func(userId string, active bool) *model.AppError {
|
|
return model.NewAppError("Activate", "mfa.activate.save_active.app_error", nil, "", http.StatusInternalServerError)
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
|
|
mfa := New(configService, &storeMock)
|
|
err := mfa.Activate(user, fmt.Sprintf("%06d", token))
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.activate.save_active.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("Successful activate", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaActive", user.Id, true).Return(func(userId string, active bool) *model.AppError {
|
|
return nil
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
mfa := New(configService, &storeMock)
|
|
|
|
err := mfa.Activate(user, fmt.Sprintf("%06d", token))
|
|
require.Nil(t, err)
|
|
})
|
|
}
|
|
|
|
func TestDeactivate(t *testing.T) {
|
|
user := &model.User{Id: model.NewId(), Roles: "system_user"}
|
|
|
|
config := model.Config{}
|
|
config.SetDefaults()
|
|
config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true)
|
|
configService := testutils.StaticConfigService{Cfg: &config}
|
|
|
|
t.Run("fail on disabled mfa", func(t *testing.T) {
|
|
wrongConfig := model.Config{}
|
|
wrongConfig.SetDefaults()
|
|
wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false)
|
|
wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig}
|
|
mfa := New(wrongConfigService, nil)
|
|
err := mfa.Deactivate(user.Id)
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.mfa_disabled.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on store UpdateMfaActive action fail", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) *model.AppError {
|
|
return model.NewAppError("Deactivate", "mfa.deactivate.save_active.app_error", nil, "", http.StatusInternalServerError)
|
|
})
|
|
userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) *model.AppError {
|
|
return model.NewAppError("Deactivate", "mfa.deactivate.save_secret.app_error", nil, "", http.StatusInternalServerError)
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
|
|
mfa := New(configService, &storeMock)
|
|
err := mfa.Deactivate(user.Id)
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.deactivate.save_active.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on store UpdateMfaSecret action fail", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) *model.AppError {
|
|
return nil
|
|
})
|
|
userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) *model.AppError {
|
|
return model.NewAppError("Deactivate", "mfa.deactivate.save_secret.app_error", nil, "", http.StatusInternalServerError)
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
|
|
mfa := New(configService, &storeMock)
|
|
err := mfa.Deactivate(user.Id)
|
|
require.NotNil(t, err)
|
|
require.Equal(t, "mfa.deactivate.save_secret.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("Successful deactivate", func(t *testing.T) {
|
|
storeMock := mocks.Store{}
|
|
userStoreMock := mocks.UserStore{}
|
|
userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) *model.AppError {
|
|
return nil
|
|
})
|
|
userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) *model.AppError {
|
|
return nil
|
|
})
|
|
storeMock.On("User").Return(&userStoreMock)
|
|
mfa := New(configService, &storeMock)
|
|
|
|
err := mfa.Deactivate(user.Id)
|
|
require.Nil(t, err)
|
|
})
|
|
}
|
|
|
|
func TestValidateToken(t *testing.T) {
|
|
secret := model.NewRandomBase32String(MFA_SECRET_SIZE)
|
|
token := dgoogauth.ComputeCode(secret, time.Now().UTC().Unix()/30)
|
|
|
|
config := model.Config{}
|
|
config.SetDefaults()
|
|
config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true)
|
|
configService := testutils.StaticConfigService{Cfg: &config}
|
|
|
|
t.Run("fail on disabled mfa", func(t *testing.T) {
|
|
wrongConfig := model.Config{}
|
|
wrongConfig.SetDefaults()
|
|
wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false)
|
|
wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig}
|
|
mfa := New(wrongConfigService, nil)
|
|
ok, err := mfa.ValidateToken(secret, fmt.Sprintf("%06d", token))
|
|
require.NotNil(t, err)
|
|
require.False(t, ok)
|
|
require.Equal(t, "mfa.mfa_disabled.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on wrongly formatted token", func(t *testing.T) {
|
|
mfa := New(configService, nil)
|
|
ok, err := mfa.ValidateToken(secret, "invalid-token")
|
|
require.NotNil(t, err)
|
|
require.False(t, ok)
|
|
require.Equal(t, "mfa.validate_token.authenticate.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("fail on invalid token", func(t *testing.T) {
|
|
mfa := New(configService, nil)
|
|
ok, err := mfa.ValidateToken(secret, "000000")
|
|
require.Nil(t, err)
|
|
require.False(t, ok)
|
|
})
|
|
|
|
t.Run("valid token", func(t *testing.T) {
|
|
mfa := New(configService, nil)
|
|
ok, err := mfa.ValidateToken(secret, fmt.Sprintf("%06d", token))
|
|
require.Nil(t, err)
|
|
require.True(t, ok)
|
|
})
|
|
}
|