github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/services/mfa/mfa_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package mfa 5 6 import ( 7 "errors" 8 "fmt" 9 "net/url" 10 "testing" 11 "time" 12 13 "github.com/dgryski/dgoogauth" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 "github.com/mattermost/mattermost-server/v5/model" 18 "github.com/mattermost/mattermost-server/v5/plugin/plugintest/mock" 19 "github.com/mattermost/mattermost-server/v5/store/storetest/mocks" 20 "github.com/mattermost/mattermost-server/v5/utils/testutils" 21 ) 22 23 func TestGenerateSecret(t *testing.T) { 24 user := &model.User{Id: model.NewId(), Roles: "system_user"} 25 26 config := model.Config{} 27 config.SetDefaults() 28 config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true) 29 configService := testutils.StaticConfigService{Cfg: &config} 30 31 t.Run("fail on disabled mfa", func(t *testing.T) { 32 wrongConfig := model.Config{} 33 wrongConfig.SetDefaults() 34 wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false) 35 wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig} 36 mfa := New(wrongConfigService, nil) 37 _, _, err := mfa.GenerateSecret(user) 38 require.NotNil(t, err) 39 require.Equal(t, "mfa.mfa_disabled.app_error", err.Id) 40 }) 41 42 t.Run("fail on store action fail", func(t *testing.T) { 43 storeMock := mocks.Store{} 44 userStoreMock := mocks.UserStore{} 45 userStoreMock.On("UpdateMfaSecret", user.Id, mock.AnythingOfType("string")).Return(func(userId string, secret string) error { 46 return errors.New("failed to update mfa secret") 47 }) 48 storeMock.On("User").Return(&userStoreMock) 49 50 mfa := New(configService, &storeMock) 51 _, _, err := mfa.GenerateSecret(user) 52 require.NotNil(t, err) 53 require.Equal(t, "mfa.generate_qr_code.save_secret.app_error", err.Id) 54 }) 55 56 t.Run("Successful generate secret", func(t *testing.T) { 57 storeMock := mocks.Store{} 58 userStoreMock := mocks.UserStore{} 59 userStoreMock.On("UpdateMfaSecret", user.Id, mock.AnythingOfType("string")).Return(func(userId string, secret string) error { 60 return nil 61 }) 62 storeMock.On("User").Return(&userStoreMock) 63 64 mfa := New(configService, &storeMock) 65 66 secret, img, err := mfa.GenerateSecret(user) 67 require.Nil(t, err) 68 assert.Len(t, secret, 32) 69 require.NotEmpty(t, img, "no image set") 70 }) 71 } 72 73 func TestGetIssuerFromUrl(t *testing.T) { 74 cases := []struct { 75 Input string 76 Expected string 77 }{ 78 {"http://somewebsite.com", url.QueryEscape("somewebsite.com")}, 79 {"https://somewebsite.com", url.QueryEscape("somewebsite.com")}, 80 {"https://some.website.com", url.QueryEscape("some.website.com")}, 81 {" https://www.somewebsite.com", url.QueryEscape("somewebsite.com")}, 82 {"http://somewebsite.com/chat", url.QueryEscape("somewebsite.com/chat")}, 83 {"somewebsite.com ", url.QueryEscape("somewebsite.com")}, 84 {"http://localhost:8065", url.QueryEscape("localhost:8065")}, 85 {"", "Mattermost"}, 86 {" ", "Mattermost"}, 87 } 88 89 for _, c := range cases { 90 assert.Equal(t, c.Expected, getIssuerFromUrl(c.Input)) 91 } 92 } 93 94 func TestActivate(t *testing.T) { 95 user := &model.User{Id: model.NewId(), Roles: "system_user"} 96 user.MfaSecret = model.NewRandomBase32String(MFASecretSize) 97 98 token := dgoogauth.ComputeCode(user.MfaSecret, time.Now().UTC().Unix()/30) 99 100 config := model.Config{} 101 config.SetDefaults() 102 config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true) 103 configService := testutils.StaticConfigService{Cfg: &config} 104 105 t.Run("fail on disabled mfa", func(t *testing.T) { 106 wrongConfig := model.Config{} 107 wrongConfig.SetDefaults() 108 wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false) 109 wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig} 110 mfa := New(wrongConfigService, nil) 111 err := mfa.Activate(user, "not-important") 112 require.NotNil(t, err) 113 require.Equal(t, "mfa.mfa_disabled.app_error", err.Id) 114 }) 115 116 t.Run("fail on wrongly formatted token", func(t *testing.T) { 117 mfa := New(configService, nil) 118 err := mfa.Activate(user, "invalid-token") 119 require.NotNil(t, err) 120 require.Equal(t, "mfa.activate.authenticate.app_error", err.Id) 121 }) 122 123 t.Run("fail on invalid token", func(t *testing.T) { 124 mfa := New(configService, nil) 125 err := mfa.Activate(user, "000000") 126 require.NotNil(t, err) 127 require.Equal(t, "mfa.activate.bad_token.app_error", err.Id) 128 }) 129 130 t.Run("fail on store action fail", func(t *testing.T) { 131 storeMock := mocks.Store{} 132 userStoreMock := mocks.UserStore{} 133 userStoreMock.On("UpdateMfaActive", user.Id, true).Return(func(userId string, active bool) error { 134 return errors.New("failed to update mfa active") 135 }) 136 storeMock.On("User").Return(&userStoreMock) 137 138 mfa := New(configService, &storeMock) 139 err := mfa.Activate(user, fmt.Sprintf("%06d", token)) 140 require.NotNil(t, err) 141 require.Equal(t, "mfa.activate.save_active.app_error", err.Id) 142 }) 143 144 t.Run("Successful activate", func(t *testing.T) { 145 storeMock := mocks.Store{} 146 userStoreMock := mocks.UserStore{} 147 userStoreMock.On("UpdateMfaActive", user.Id, true).Return(func(userId string, active bool) error { 148 return nil 149 }) 150 storeMock.On("User").Return(&userStoreMock) 151 mfa := New(configService, &storeMock) 152 153 err := mfa.Activate(user, fmt.Sprintf("%06d", token)) 154 require.Nil(t, err) 155 }) 156 } 157 158 func TestDeactivate(t *testing.T) { 159 user := &model.User{Id: model.NewId(), Roles: "system_user"} 160 161 config := model.Config{} 162 config.SetDefaults() 163 config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true) 164 configService := testutils.StaticConfigService{Cfg: &config} 165 166 t.Run("fail on disabled mfa", func(t *testing.T) { 167 wrongConfig := model.Config{} 168 wrongConfig.SetDefaults() 169 wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false) 170 wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig} 171 mfa := New(wrongConfigService, nil) 172 err := mfa.Deactivate(user.Id) 173 require.NotNil(t, err) 174 require.Equal(t, "mfa.mfa_disabled.app_error", err.Id) 175 }) 176 177 t.Run("fail on store UpdateMfaActive action fail", func(t *testing.T) { 178 storeMock := mocks.Store{} 179 userStoreMock := mocks.UserStore{} 180 userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) error { 181 return errors.New("failed to update mfa active") 182 }) 183 userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) error { 184 return errors.New("failed to update mfa secret") 185 }) 186 storeMock.On("User").Return(&userStoreMock) 187 188 mfa := New(configService, &storeMock) 189 err := mfa.Deactivate(user.Id) 190 require.NotNil(t, err) 191 require.Equal(t, "mfa.deactivate.save_active.app_error", err.Id) 192 }) 193 194 t.Run("fail on store UpdateMfaSecret action fail", func(t *testing.T) { 195 storeMock := mocks.Store{} 196 userStoreMock := mocks.UserStore{} 197 userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) error { 198 return nil 199 }) 200 userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) error { 201 return errors.New("failed to update mfa secret") 202 }) 203 storeMock.On("User").Return(&userStoreMock) 204 205 mfa := New(configService, &storeMock) 206 err := mfa.Deactivate(user.Id) 207 require.NotNil(t, err) 208 require.Equal(t, "mfa.deactivate.save_secret.app_error", err.Id) 209 }) 210 211 t.Run("Successful deactivate", func(t *testing.T) { 212 storeMock := mocks.Store{} 213 userStoreMock := mocks.UserStore{} 214 userStoreMock.On("UpdateMfaActive", user.Id, false).Return(func(userId string, active bool) error { 215 return nil 216 }) 217 userStoreMock.On("UpdateMfaSecret", user.Id, "").Return(func(userId string, secret string) error { 218 return nil 219 }) 220 storeMock.On("User").Return(&userStoreMock) 221 mfa := New(configService, &storeMock) 222 223 err := mfa.Deactivate(user.Id) 224 require.Nil(t, err) 225 }) 226 } 227 228 func TestValidateToken(t *testing.T) { 229 secret := model.NewRandomBase32String(MFASecretSize) 230 token := dgoogauth.ComputeCode(secret, time.Now().UTC().Unix()/30) 231 232 config := model.Config{} 233 config.SetDefaults() 234 config.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(true) 235 configService := testutils.StaticConfigService{Cfg: &config} 236 237 t.Run("fail on disabled mfa", func(t *testing.T) { 238 wrongConfig := model.Config{} 239 wrongConfig.SetDefaults() 240 wrongConfig.ServiceSettings.EnableMultifactorAuthentication = model.NewBool(false) 241 wrongConfigService := testutils.StaticConfigService{Cfg: &wrongConfig} 242 mfa := New(wrongConfigService, nil) 243 ok, err := mfa.ValidateToken(secret, fmt.Sprintf("%06d", token)) 244 require.NotNil(t, err) 245 require.False(t, ok) 246 require.Equal(t, "mfa.mfa_disabled.app_error", err.Id) 247 }) 248 249 t.Run("fail on wrongly formatted token", func(t *testing.T) { 250 mfa := New(configService, nil) 251 ok, err := mfa.ValidateToken(secret, "invalid-token") 252 require.NotNil(t, err) 253 require.False(t, ok) 254 require.Equal(t, "mfa.validate_token.authenticate.app_error", err.Id) 255 }) 256 257 t.Run("fail on invalid token", func(t *testing.T) { 258 mfa := New(configService, nil) 259 ok, err := mfa.ValidateToken(secret, "000000") 260 require.Nil(t, err) 261 require.False(t, ok) 262 }) 263 264 t.Run("valid token", func(t *testing.T) { 265 mfa := New(configService, nil) 266 ok, err := mfa.ValidateToken(secret, fmt.Sprintf("%06d", token)) 267 require.Nil(t, err) 268 require.True(t, ok) 269 }) 270 }