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  }