The TestValidateToken was actually testing the underlying library rather than testing our code. So, in a way, it was ineffective. The actual flaky test was that in a rare care, the random string generated might actually lead to 000000 being an actual code rather than invalid. So using random strings is fundamentally incorrect. Even the tests in the library use hardcoded strings and not random strings. To fix this properly would be to use hardcoded strings, but then we would just be testing the library and not our code. To keep things simple, we just keep the test to verify the error message and remove the others. https://community-daily.mattermost.com/boards/workspace/zyoahc9uapdn3xdptac6jb69ic/285b80a3-257d-41f6-8cf4-ed80ca9d92e5/495cdb4d-c13a-4992-8eb9-80cfee2819a4/cidjhgy1ag3yktn5eszae1489yr ```release-note NONE ```
172 строки
5.4 KiB
Go
172 строки
5.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package mfa
|
|
|
|
import (
|
|
"encoding/base32"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dgryski/dgoogauth"
|
|
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGenerateSecret(t *testing.T) {
|
|
userID := "user-id"
|
|
userEmail := "sample@sample.com"
|
|
siteURL := "http://localhost:8065"
|
|
|
|
t.Run("fail on store action fail", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaSecret", userID, mock.AnythingOfType("string")).Return(func(userId string, secret string) error {
|
|
return errors.New("failed to update mfa secret")
|
|
})
|
|
|
|
_, _, err := New(&storeMock).GenerateSecret(siteURL, userEmail, userID)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unable to store mfa secret")
|
|
})
|
|
|
|
t.Run("Successful generate secret", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaSecret", userID, mock.AnythingOfType("string")).Return(func(userId string, secret string) error {
|
|
return nil
|
|
})
|
|
|
|
secret, img, err := New(&storeMock).GenerateSecret(siteURL, userEmail, userID)
|
|
require.NoError(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) {
|
|
userID := "user-id"
|
|
userMfaSecret := newRandomBase32String(mfaSecretSize)
|
|
|
|
token := dgoogauth.ComputeCode(userMfaSecret, time.Now().UTC().Unix()/30)
|
|
|
|
t.Run("fail on wrongly formatted token", func(t *testing.T) {
|
|
err := New(nil).Activate(userMfaSecret, userID, "invalid-token")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unable to parse the token")
|
|
})
|
|
|
|
t.Run("fail on invalid token", func(t *testing.T) {
|
|
err := New(nil).Activate(userMfaSecret, userID, "000000")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid mfa token")
|
|
})
|
|
|
|
t.Run("fail on store action fail", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaActive", userID, true).Return(func(userId string, active bool) error {
|
|
return errors.New("failed to update mfa active")
|
|
})
|
|
|
|
err := New(&storeMock).Activate(userMfaSecret, userID, fmt.Sprintf("%06d", token))
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unable to store mfa active")
|
|
})
|
|
|
|
t.Run("Successful activate", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaActive", userID, true).Return(func(userId string, active bool) error {
|
|
return nil
|
|
})
|
|
|
|
err := New(&storeMock).Activate(userMfaSecret, userID, fmt.Sprintf("%06d", token))
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestDeactivate(t *testing.T) {
|
|
userID := "user-id"
|
|
|
|
t.Run("fail on store UpdateMfaActive action fail", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error {
|
|
return errors.New("failed to update mfa active")
|
|
})
|
|
storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error {
|
|
return errors.New("failed to update mfa secret")
|
|
})
|
|
|
|
err := New(&storeMock).Deactivate(userID)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unable to store mfa active")
|
|
})
|
|
|
|
t.Run("fail on store UpdateMfaSecret action fail", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error {
|
|
return nil
|
|
})
|
|
storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error {
|
|
return errors.New("failed to update mfa secret")
|
|
})
|
|
|
|
err := New(&storeMock).Deactivate(userID)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unable to store mfa secret")
|
|
})
|
|
|
|
t.Run("Successful deactivate", func(t *testing.T) {
|
|
storeMock := mocks.UserStore{}
|
|
storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error {
|
|
return nil
|
|
})
|
|
storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error {
|
|
return nil
|
|
})
|
|
|
|
err := New(&storeMock).Deactivate(userID)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestValidateToken(t *testing.T) {
|
|
t.Run("fail on wrongly formatted token", func(t *testing.T) {
|
|
secret := newRandomBase32String(mfaSecretSize)
|
|
ok, err := New(nil).ValidateToken(secret, "invalid-token")
|
|
require.Error(t, err)
|
|
require.False(t, ok)
|
|
require.Contains(t, err.Error(), "unable to parse the token")
|
|
})
|
|
}
|
|
|
|
func TestRandomBase32String(t *testing.T) {
|
|
for i := 0; i < 1000; i++ {
|
|
str := newRandomBase32String(i)
|
|
require.Len(t, str, base32.StdEncoding.EncodedLen(i))
|
|
}
|
|
}
|