From 7a291894f6010b5a8f7094fef931ce72e3cb7cce Mon Sep 17 00:00:00 2001 From: Casper Biering Date: Wed, 2 Sep 2020 12:31:17 +0200 Subject: [PATCH 1/5] Allow password to be saved to keychain with skip-prompt --- cmd/saml2aws/commands/configure.go | 41 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/cmd/saml2aws/commands/configure.go b/cmd/saml2aws/commands/configure.go index feddd6f64..83d44574b 100644 --- a/cmd/saml2aws/commands/configure.go +++ b/cmd/saml2aws/commands/configure.go @@ -21,6 +21,7 @@ const OneLoginOAuthPath = "/auth/oauth2/v2/token" func Configure(configFlags *flags.CommonFlags) error { idpAccountName := configFlags.IdpAccount + idpAccountPassword := configFlags.Password // pass in alternative location of saml2aws config file, if set. cfgm, err := cfg.NewConfigManager(configFlags.ConfigFile) @@ -43,13 +44,27 @@ func Configure(configFlags *flags.CommonFlags) error { return errors.Wrap(err, "failed to input configuration") } - if credentials.SupportsStorage() { - if err := storeCredentials(configFlags, account); err != nil { - return err + if credentials.SupportsStorage() && idpAccountPassword == "" { + password := prompter.Password("Password") + if password != "" { + if confirmPassword := prompter.Password("Confirm"); confirmPassword == password { + idpAccountPassword = password + } else { + log.Println("Passwords did not match") + os.Exit(1) + } + } else { + log.Println("No password supplied") } } } + if credentials.SupportsStorage() { + if err := storeCredentials(configFlags, account, idpAccountPassword); err != nil { + return err + } + } + err = cfgm.SaveIDPAccount(idpAccountName, account) if err != nil { return errors.Wrap(err, "failed to save configuration") @@ -63,28 +78,16 @@ func Configure(configFlags *flags.CommonFlags) error { return nil } -func storeCredentials(configFlags *flags.CommonFlags, account *cfg.IDPAccount) error { +func storeCredentials(configFlags *flags.CommonFlags, account *cfg.IDPAccount, idpAccountPassword string) error { if configFlags.DisableKeychain { return nil } - if configFlags.Password != "" { - if err := credentials.SaveCredentials(account.URL, account.Username, configFlags.Password); err != nil { + if idpAccountPassword != "" { + if err := credentials.SaveCredentials(account.URL, account.Username, idpAccountPassword); err != nil { return errors.Wrap(err, "error storing password in keychain") } } else { - password := prompter.Password("Password") - if password != "" { - if confirmPassword := prompter.Password("Confirm"); confirmPassword == password { - if err := credentials.SaveCredentials(account.URL, account.Username, password); err != nil { - return errors.Wrap(err, "error storing password in keychain") - } - } else { - log.Println("Passwords did not match") - os.Exit(1) - } - } else { - log.Println("No password supplied") - } + log.Println("No password supplied") } if account.Provider == onelogin.ProviderName { if configFlags.ClientID == "" || configFlags.ClientSecret == "" { From ed4c6c8c469c2f44def91b917c76a68b2c735c8e Mon Sep 17 00:00:00 2001 From: Tina Boyce <12929562+tinaboyce@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:36:16 +1100 Subject: [PATCH 2/5] Bump test coverage of storeCredentials module --- cmd/saml2aws/commands/configure_test.go | 192 ++++++++++++++++++++++++ mocks/Helper.go | 116 ++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 cmd/saml2aws/commands/configure_test.go create mode 100644 mocks/Helper.go diff --git a/cmd/saml2aws/commands/configure_test.go b/cmd/saml2aws/commands/configure_test.go new file mode 100644 index 000000000..29f13c951 --- /dev/null +++ b/cmd/saml2aws/commands/configure_test.go @@ -0,0 +1,192 @@ +package commands + +import ( + "os" + "os/exec" + "path" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/versent/saml2aws/v2/helper/credentials" + "github.com/versent/saml2aws/v2/mocks" + "github.com/versent/saml2aws/v2/pkg/cfg" + "github.com/versent/saml2aws/v2/pkg/flags" + "github.com/versent/saml2aws/v2/pkg/provider/onelogin" +) + +// Store Credentials module +func TestStoreCredentialsOnDisabledKeychainFlagReturnsNil(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: true} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: "Ping", + Username: "wolfeidau", + } + + result := storeCredentials(commonFlags, idpAccount, "password") + + assert.Nil(t, result) +} + +func TestStoreCredentialsOnProvidedPasswordSavesCredentials(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: "Ping", + Username: "wolfeidau", + } + creds := &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"} + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", creds).Return(nil).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + result := storeCredentials(commonFlags, idpAccount, "password") + + helperMock.AssertCalled(t, "Add", creds) + assert.Nil(t, result) + credentials.CurrentHelper = oldCurrentHelper +} + +func TestStoreCredentialsOnProvidedPasswordHandlesErrorOnSavesCredentials(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: "Ping", + Username: "wolfeidau", + } + creds := &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"} + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", creds).Return(errors.New("i am an error")).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + result := storeCredentials(commonFlags, idpAccount, "password") + + helperMock.AssertCalled(t, "Add", creds) + assert.ErrorContains(t, result, "i am an error") + assert.ErrorContains(t, result, "error storing password in keychain") + credentials.CurrentHelper = oldCurrentHelper +} + +func TestStoreCredentialsOnMissingPasswordSkipsSavingCredentials(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: "Ping", + Username: "wolfeidau", + } + creds := &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"} + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", creds).Return(nil).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + result := storeCredentials(commonFlags, idpAccount, "") + + helperMock.AssertNotCalled(t, "Add") + assert.Nil(t, result) + credentials.CurrentHelper = oldCurrentHelper +} + +func TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false, ClientID: "", ClientSecret: "oneloginSecret"} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: onelogin.ProviderName, + Username: "wolfeidau", + } + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", mock.Anything).Return(nil).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + if os.Getenv("BE_CRASHER") == "1" { + storeCredentials(commonFlags, idpAccount, "password") + return + } + cmd := exec.Command(os.Args[0], "-test.run=TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram") + cmd.Env = append(os.Environ(), "BE_CRASHER=1") + err := cmd.Run() + if e, ok := err.(*exec.ExitError); ok && !e.Success() { + credentials.CurrentHelper = oldCurrentHelper + return + } + t.Fatalf("process ran with err %v, want exit status 1", err) +} + +func TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false, ClientID: "oneloginSecret", ClientSecret: ""} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: onelogin.ProviderName, + Username: "wolfeidau", + } + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", mock.Anything).Return(nil).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + if os.Getenv("BE_CRASHER") == "1" { + storeCredentials(commonFlags, idpAccount, "password") + return + } + cmd := exec.Command(os.Args[0], "-test.run=TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram") + cmd.Env = append(os.Environ(), "BE_CRASHER=1") + err := cmd.Run() + if e, ok := err.(*exec.ExitError); ok && !e.Success() { + credentials.CurrentHelper = oldCurrentHelper + return + } + t.Fatalf("process ran with err %v, want exit status 1", err) +} + +func TestStoreCredentialsOnProvidedOneLoginSavesCredentials(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false, ClientID: "oneloginId", ClientSecret: "oneloginSecret"} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: onelogin.ProviderName, + Username: "wolfeidau", + } + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"}).Return(nil).Once() + helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: path.Join("https://id.example.com", OneLoginOAuthPath), Username: "oneloginId", Secret: "oneloginSecret"}).Return(nil).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + result := storeCredentials(commonFlags, idpAccount, "password") + + helperMock.AssertNumberOfCalls(t, "Add", 2) + assert.Nil(t, result) + credentials.CurrentHelper = oldCurrentHelper +} + +func TestStoreCredentialsOnProvidedOneLoginHandlesErrorOnSavesCredentials(t *testing.T) { + commonFlags := &flags.CommonFlags{DisableKeychain: false, ClientID: "oneloginId", ClientSecret: "oneloginSecret"} + idpAccount := &cfg.IDPAccount{ + URL: "https://id.example.com", + MFA: "none", + Provider: onelogin.ProviderName, + Username: "wolfeidau", + } + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"}).Return(nil).Once() + helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: path.Join("https://id.example.com", OneLoginOAuthPath), Username: "oneloginId", Secret: "oneloginSecret"}).Return(errors.New("failed again")).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + result := storeCredentials(commonFlags, idpAccount, "password") + + assert.ErrorContains(t, result, "failed again") + assert.ErrorContains(t, result, "error storing client_id and client_secret in keychain") + credentials.CurrentHelper = oldCurrentHelper +} diff --git a/mocks/Helper.go b/mocks/Helper.go new file mode 100644 index 000000000..5dda25373 --- /dev/null +++ b/mocks/Helper.go @@ -0,0 +1,116 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + credentials "github.com/versent/saml2aws/v2/helper/credentials" +) + +// Helper is an autogenerated mock type for the Helper type +type Helper struct { + mock.Mock +} + +// Add provides a mock function with given fields: _a0 +func (_m *Helper) Add(_a0 *credentials.Credentials) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Add") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*credentials.Credentials) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Delete provides a mock function with given fields: serverURL +func (_m *Helper) Delete(serverURL string) error { + ret := _m.Called(serverURL) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(serverURL) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: serverURL +func (_m *Helper) Get(serverURL string) (string, string, error) { + ret := _m.Called(serverURL) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 string + var r1 string + var r2 error + if rf, ok := ret.Get(0).(func(string) (string, string, error)); ok { + return rf(serverURL) + } + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(serverURL) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string) string); ok { + r1 = rf(serverURL) + } else { + r1 = ret.Get(1).(string) + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(serverURL) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// SupportsCredentialStorage provides a mock function with given fields: +func (_m *Helper) SupportsCredentialStorage() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SupportsCredentialStorage") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// NewHelper creates a new instance of Helper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewHelper(t interface { + mock.TestingT + Cleanup(func()) +}) *Helper { + mock := &Helper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 4c09fe794d9985b364442b41a6e976d715e2cb6f Mon Sep 17 00:00:00 2001 From: Tina Boyce <12929562+tinaboyce@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:49:41 +1100 Subject: [PATCH 3/5] Fix lint issue with configure testing file --- cmd/saml2aws/commands/configure_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/saml2aws/commands/configure_test.go b/cmd/saml2aws/commands/configure_test.go index 29f13c951..9cbd75d6c 100644 --- a/cmd/saml2aws/commands/configure_test.go +++ b/cmd/saml2aws/commands/configure_test.go @@ -109,7 +109,11 @@ func TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram(t *testing.T) { credentials.CurrentHelper = helperMock if os.Getenv("BE_CRASHER") == "1" { - storeCredentials(commonFlags, idpAccount, "password") + err := storeCredentials(commonFlags, idpAccount, "password") + // making linter happy + if err != nil { + return + } return } cmd := exec.Command(os.Args[0], "-test.run=TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram") @@ -136,7 +140,11 @@ func TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram(t *testing.T) credentials.CurrentHelper = helperMock if os.Getenv("BE_CRASHER") == "1" { - storeCredentials(commonFlags, idpAccount, "password") + err := storeCredentials(commonFlags, idpAccount, "password") + // making linter happy + if err != nil { + return + } return } cmd := exec.Command(os.Args[0], "-test.run=TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram") From a4b03621f6a46fdf1a36c35567259321cb48d725 Mon Sep 17 00:00:00 2001 From: Tina Boyce <12929562+tinaboyce@users.noreply.github.com> Date: Thu, 28 Dec 2023 01:21:18 +1100 Subject: [PATCH 4/5] Add configure module test --- cmd/saml2aws/commands/configure_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cmd/saml2aws/commands/configure_test.go b/cmd/saml2aws/commands/configure_test.go index 9cbd75d6c..e84efa7bb 100644 --- a/cmd/saml2aws/commands/configure_test.go +++ b/cmd/saml2aws/commands/configure_test.go @@ -16,6 +16,26 @@ import ( "github.com/versent/saml2aws/v2/pkg/provider/onelogin" ) +// Configure module +func TestConfigureStoresCredentialOnSupportedStorage(t *testing.T) { + commonFlags := &flags.CommonFlags{URL: "https://id.example.com", Username: "some-username", Password: "password", SkipPrompt: true} + creds := &credentials.Credentials{ServerURL: "https://id.example.com", Username: "some-username", Secret: "password"} + helperMock := &mocks.Helper{} + helperMock.Mock.On("Add", creds).Return(nil).Once() + helperMock.Mock.On("SupportsCredentialStorage").Return(true).Once() + oldCurrentHelper := credentials.CurrentHelper + credentials.CurrentHelper = helperMock + + err := Configure(commonFlags) + // making linter happy + if err != nil { + credentials.CurrentHelper = oldCurrentHelper + } + + helperMock.AssertCalled(t, "Add", creds) + credentials.CurrentHelper = oldCurrentHelper +} + // Store Credentials module func TestStoreCredentialsOnDisabledKeychainFlagReturnsNil(t *testing.T) { commonFlags := &flags.CommonFlags{DisableKeychain: true} From 9748742a0488a538f7e5c6ae74236f544cd9eb85 Mon Sep 17 00:00:00 2001 From: Tina Boyce <12929562+tinaboyce@users.noreply.github.com> Date: Thu, 28 Dec 2023 21:26:19 +1100 Subject: [PATCH 5/5] Refactor configure test by utilising defer --- cmd/saml2aws/commands/configure_test.go | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/cmd/saml2aws/commands/configure_test.go b/cmd/saml2aws/commands/configure_test.go index e84efa7bb..6250362e4 100644 --- a/cmd/saml2aws/commands/configure_test.go +++ b/cmd/saml2aws/commands/configure_test.go @@ -63,13 +63,15 @@ func TestStoreCredentialsOnProvidedPasswordSavesCredentials(t *testing.T) { helperMock := &mocks.Helper{} helperMock.Mock.On("Add", creds).Return(nil).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock result := storeCredentials(commonFlags, idpAccount, "password") helperMock.AssertCalled(t, "Add", creds) assert.Nil(t, result) - credentials.CurrentHelper = oldCurrentHelper } func TestStoreCredentialsOnProvidedPasswordHandlesErrorOnSavesCredentials(t *testing.T) { @@ -84,6 +86,9 @@ func TestStoreCredentialsOnProvidedPasswordHandlesErrorOnSavesCredentials(t *tes helperMock := &mocks.Helper{} helperMock.Mock.On("Add", creds).Return(errors.New("i am an error")).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock result := storeCredentials(commonFlags, idpAccount, "password") @@ -91,7 +96,6 @@ func TestStoreCredentialsOnProvidedPasswordHandlesErrorOnSavesCredentials(t *tes helperMock.AssertCalled(t, "Add", creds) assert.ErrorContains(t, result, "i am an error") assert.ErrorContains(t, result, "error storing password in keychain") - credentials.CurrentHelper = oldCurrentHelper } func TestStoreCredentialsOnMissingPasswordSkipsSavingCredentials(t *testing.T) { @@ -106,13 +110,15 @@ func TestStoreCredentialsOnMissingPasswordSkipsSavingCredentials(t *testing.T) { helperMock := &mocks.Helper{} helperMock.Mock.On("Add", creds).Return(nil).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock result := storeCredentials(commonFlags, idpAccount, "") helperMock.AssertNotCalled(t, "Add") assert.Nil(t, result) - credentials.CurrentHelper = oldCurrentHelper } func TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram(t *testing.T) { @@ -126,6 +132,9 @@ func TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram(t *testing.T) { helperMock := &mocks.Helper{} helperMock.Mock.On("Add", mock.Anything).Return(nil).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock if os.Getenv("BE_CRASHER") == "1" { @@ -140,7 +149,6 @@ func TestStoreCredentialsOnMissingOneLoginClientIdExitsProgram(t *testing.T) { cmd.Env = append(os.Environ(), "BE_CRASHER=1") err := cmd.Run() if e, ok := err.(*exec.ExitError); ok && !e.Success() { - credentials.CurrentHelper = oldCurrentHelper return } t.Fatalf("process ran with err %v, want exit status 1", err) @@ -157,6 +165,9 @@ func TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram(t *testing.T) helperMock := &mocks.Helper{} helperMock.Mock.On("Add", mock.Anything).Return(nil).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock if os.Getenv("BE_CRASHER") == "1" { @@ -171,7 +182,6 @@ func TestStoreCredentialsOnMissingOneLoginClientSecretExitsProgram(t *testing.T) cmd.Env = append(os.Environ(), "BE_CRASHER=1") err := cmd.Run() if e, ok := err.(*exec.ExitError); ok && !e.Success() { - credentials.CurrentHelper = oldCurrentHelper return } t.Fatalf("process ran with err %v, want exit status 1", err) @@ -189,13 +199,15 @@ func TestStoreCredentialsOnProvidedOneLoginSavesCredentials(t *testing.T) { helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"}).Return(nil).Once() helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: path.Join("https://id.example.com", OneLoginOAuthPath), Username: "oneloginId", Secret: "oneloginSecret"}).Return(nil).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock result := storeCredentials(commonFlags, idpAccount, "password") helperMock.AssertNumberOfCalls(t, "Add", 2) assert.Nil(t, result) - credentials.CurrentHelper = oldCurrentHelper } func TestStoreCredentialsOnProvidedOneLoginHandlesErrorOnSavesCredentials(t *testing.T) { @@ -210,11 +222,13 @@ func TestStoreCredentialsOnProvidedOneLoginHandlesErrorOnSavesCredentials(t *tes helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: "https://id.example.com", Username: "wolfeidau", Secret: "password"}).Return(nil).Once() helperMock.Mock.On("Add", &credentials.Credentials{ServerURL: path.Join("https://id.example.com", OneLoginOAuthPath), Username: "oneloginId", Secret: "oneloginSecret"}).Return(errors.New("failed again")).Once() oldCurrentHelper := credentials.CurrentHelper + defer func() { + credentials.CurrentHelper = oldCurrentHelper + }() credentials.CurrentHelper = helperMock result := storeCredentials(commonFlags, idpAccount, "password") assert.ErrorContains(t, result, "failed again") assert.ErrorContains(t, result, "error storing client_id and client_secret in keychain") - credentials.CurrentHelper = oldCurrentHelper }