github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/shared/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 "encoding/base32" 8 "errors" 9 "fmt" 10 "net/url" 11 "testing" 12 "time" 13 14 "github.com/dgryski/dgoogauth" 15 "github.com/masterhung0112/hk_server/v5/plugin/plugintest/mock" 16 "github.com/masterhung0112/hk_server/v5/store/storetest/mocks" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestGenerateSecret(t *testing.T) { 22 userID := "user-id" 23 userEmail := "sample@sample.com" 24 siteURL := "http://localhost:8065" 25 26 t.Run("fail on store action fail", func(t *testing.T) { 27 storeMock := mocks.UserStore{} 28 storeMock.On("UpdateMfaSecret", userID, mock.AnythingOfType("string")).Return(func(userId string, secret string) error { 29 return errors.New("failed to update mfa secret") 30 }) 31 32 _, _, err := New(&storeMock).GenerateSecret(siteURL, userEmail, userID) 33 require.Error(t, err) 34 require.Contains(t, err.Error(), "unable to store mfa secret") 35 }) 36 37 t.Run("Successful generate secret", func(t *testing.T) { 38 storeMock := mocks.UserStore{} 39 storeMock.On("UpdateMfaSecret", userID, mock.AnythingOfType("string")).Return(func(userId string, secret string) error { 40 return nil 41 }) 42 43 secret, img, err := New(&storeMock).GenerateSecret(siteURL, userEmail, userID) 44 require.NoError(t, err) 45 assert.Len(t, secret, 32) 46 require.NotEmpty(t, img, "no image set") 47 }) 48 } 49 50 func TestGetIssuerFromUrl(t *testing.T) { 51 cases := []struct { 52 Input string 53 Expected string 54 }{ 55 {"http://somewebsite.com", url.QueryEscape("somewebsite.com")}, 56 {"https://somewebsite.com", url.QueryEscape("somewebsite.com")}, 57 {"https://some.website.com", url.QueryEscape("some.website.com")}, 58 {" https://www.somewebsite.com", url.QueryEscape("somewebsite.com")}, 59 {"http://somewebsite.com/chat", url.QueryEscape("somewebsite.com/chat")}, 60 {"somewebsite.com ", url.QueryEscape("somewebsite.com")}, 61 {"http://localhost:8065", url.QueryEscape("localhost:8065")}, 62 {"", "Mattermost"}, 63 {" ", "Mattermost"}, 64 } 65 66 for _, c := range cases { 67 assert.Equal(t, c.Expected, getIssuerFromUrl(c.Input)) 68 } 69 } 70 71 func TestActivate(t *testing.T) { 72 userID := "user-id" 73 userMfaSecret := newRandomBase32String(mfaSecretSize) 74 75 token := dgoogauth.ComputeCode(userMfaSecret, time.Now().UTC().Unix()/30) 76 77 t.Run("fail on wrongly formatted token", func(t *testing.T) { 78 err := New(nil).Activate(userMfaSecret, userID, "invalid-token") 79 require.Error(t, err) 80 require.Contains(t, err.Error(), "unable to parse the token") 81 }) 82 83 t.Run("fail on invalid token", func(t *testing.T) { 84 err := New(nil).Activate(userMfaSecret, userID, "000000") 85 require.Error(t, err) 86 require.Contains(t, err.Error(), "invalid mfa token") 87 }) 88 89 t.Run("fail on store action fail", func(t *testing.T) { 90 storeMock := mocks.UserStore{} 91 storeMock.On("UpdateMfaActive", userID, true).Return(func(userId string, active bool) error { 92 return errors.New("failed to update mfa active") 93 }) 94 95 err := New(&storeMock).Activate(userMfaSecret, userID, fmt.Sprintf("%06d", token)) 96 require.Error(t, err) 97 require.Contains(t, err.Error(), "unable to store mfa active") 98 }) 99 100 t.Run("Successful activate", func(t *testing.T) { 101 storeMock := mocks.UserStore{} 102 storeMock.On("UpdateMfaActive", userID, true).Return(func(userId string, active bool) error { 103 return nil 104 }) 105 106 err := New(&storeMock).Activate(userMfaSecret, userID, fmt.Sprintf("%06d", token)) 107 require.NoError(t, err) 108 }) 109 } 110 111 func TestDeactivate(t *testing.T) { 112 userID := "user-id" 113 114 t.Run("fail on store UpdateMfaActive action fail", func(t *testing.T) { 115 storeMock := mocks.UserStore{} 116 storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error { 117 return errors.New("failed to update mfa active") 118 }) 119 storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error { 120 return errors.New("failed to update mfa secret") 121 }) 122 123 err := New(&storeMock).Deactivate(userID) 124 require.Error(t, err) 125 require.Contains(t, err.Error(), "unable to store mfa active") 126 }) 127 128 t.Run("fail on store UpdateMfaSecret action fail", func(t *testing.T) { 129 storeMock := mocks.UserStore{} 130 storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error { 131 return nil 132 }) 133 storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error { 134 return errors.New("failed to update mfa secret") 135 }) 136 137 err := New(&storeMock).Deactivate(userID) 138 require.Error(t, err) 139 require.Contains(t, err.Error(), "unable to store mfa secret") 140 }) 141 142 t.Run("Successful deactivate", func(t *testing.T) { 143 storeMock := mocks.UserStore{} 144 storeMock.On("UpdateMfaActive", userID, false).Return(func(userId string, active bool) error { 145 return nil 146 }) 147 storeMock.On("UpdateMfaSecret", userID, "").Return(func(userId string, secret string) error { 148 return nil 149 }) 150 151 err := New(&storeMock).Deactivate(userID) 152 require.NoError(t, err) 153 }) 154 } 155 156 func TestValidateToken(t *testing.T) { 157 secret := newRandomBase32String(mfaSecretSize) 158 token := dgoogauth.ComputeCode(secret, time.Now().UTC().Unix()/30) 159 160 t.Run("fail on wrongly formatted token", func(t *testing.T) { 161 ok, err := New(nil).ValidateToken(secret, "invalid-token") 162 require.Error(t, err) 163 require.False(t, ok) 164 require.Contains(t, err.Error(), "unable to parse the token") 165 }) 166 167 t.Run("fail on invalid token", func(t *testing.T) { 168 ok, err := New(nil).ValidateToken(secret, "000000") 169 require.NoError(t, err) 170 require.False(t, ok) 171 }) 172 173 t.Run("valid token", func(t *testing.T) { 174 ok, err := New(nil).ValidateToken(secret, fmt.Sprintf("%06d", token)) 175 require.NoError(t, err) 176 require.True(t, ok) 177 }) 178 } 179 180 func TestRandomBase32String(t *testing.T) { 181 for i := 0; i < 1000; i++ { 182 str := newRandomBase32String(i) 183 require.Len(t, str, base32.StdEncoding.EncodedLen(i)) 184 } 185 }