diff --git a/cmd/config/get_authinfo.go b/cmd/config/get_authinfo.go index 80f2a6e5a..ae8368fb8 100644 --- a/cmd/config/get_authinfo.go +++ b/cmd/config/get_authinfo.go @@ -60,7 +60,10 @@ func NewGetAuthInfoCommand(rootSettings *environment.AirshipCTLSettings) *cobra. } fmt.Fprintln(cmd.OutOrStdout(), authinfo) } else { - authinfos := airconfig.GetAuthInfos() + authinfos, err := airconfig.GetAuthInfos() + if err != nil { + return err + } if len(authinfos) == 0 { fmt.Fprintln(cmd.OutOrStdout(), "No User credentials found in the configuration.") } diff --git a/cmd/config/get_authinfo_test.go b/cmd/config/get_authinfo_test.go index 43b927116..132976155 100644 --- a/cmd/config/get_authinfo_test.go +++ b/cmd/config/get_authinfo_test.go @@ -20,6 +20,8 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + kubeconfig "k8s.io/client-go/tools/clientcmd/api" cmd "opendev.org/airship/airshipctl/cmd/config" @@ -39,9 +41,15 @@ func TestGetAuthInfoCmd(t *testing.T) { settings := &environment.AirshipCTLSettings{ Config: &config.Config{ AuthInfos: map[string]*config.AuthInfo{ - fooAuthInfo: getTestAuthInfo(), - barAuthInfo: getTestAuthInfo(), - bazAuthInfo: getTestAuthInfo(), + fooAuthInfo: getTestAuthInfo(fooAuthInfo), + }, + }, + } + settingsWithMultipleAuth := &environment.AirshipCTLSettings{ + Config: &config.Config{ + AuthInfos: map[string]*config.AuthInfo{ + barAuthInfo: getTestAuthInfo(barAuthInfo), + bazAuthInfo: getTestAuthInfo(bazAuthInfo), }, }, } @@ -53,8 +61,9 @@ func TestGetAuthInfoCmd(t *testing.T) { Cmd: cmd.NewGetAuthInfoCommand(settings), }, { - Name: "get-all-credentials", - Cmd: cmd.NewGetAuthInfoCommand(settings), + Name: "get-all-credentials", + CmdLine: "", + Cmd: cmd.NewGetAuthInfoCommand(settingsWithMultipleAuth), }, { Name: "missing", @@ -80,17 +89,31 @@ func TestNoAuthInfosGetAuthInfoCmd(t *testing.T) { testutil.RunTest(t, cmdTest) } -func getTestAuthInfo() *config.AuthInfo { +func TestDecodeAuthInfo(t *testing.T) { + _, err := config.DecodeAuthInfo(&kubeconfig.AuthInfo{Password: "dummy_password"}) + assert.Error(t, err, config.ErrDecodingCredentials{Given: "dummy_password"}) + + _, err = config.DecodeAuthInfo(&kubeconfig.AuthInfo{ClientCertificate: "dummy_certificate"}) + assert.Error(t, err, config.ErrDecodingCredentials{Given: "dummy_certificate"}) + + _, err = config.DecodeAuthInfo(&kubeconfig.AuthInfo{ClientKey: "dummy_key"}) + assert.Error(t, err, config.ErrDecodingCredentials{Given: "dummy_key"}) + + _, err = config.DecodeAuthInfo(&kubeconfig.AuthInfo{Token: "dummy_token"}) + assert.Error(t, err, config.ErrDecodingCredentials{Given: "dummy_token"}) +} + +func getTestAuthInfo(authName string) *config.AuthInfo { kAuthInfo := &kubeconfig.AuthInfo{ - Username: "dummy_user", - Password: "dummy_password", - ClientCertificate: "dummy_certificate", - ClientKey: "dummy_key", - Token: "dummy_token", + Username: authName + "_user", + Password: authName + "_password", + ClientCertificate: authName + "_certificate", + ClientKey: authName + "_key", + Token: authName + "_token", } newAuthInfo := &config.AuthInfo{} - newAuthInfo.SetKubeAuthInfo(kAuthInfo) - + encodedKAuthInfo := config.EncodeAuthInfo(kAuthInfo) + newAuthInfo.SetKubeAuthInfo(encodedKAuthInfo) return newAuthInfo } diff --git a/cmd/config/set_authinfo_test.go b/cmd/config/set_authinfo_test.go index 27b31facd..6ba577fd5 100644 --- a/cmd/config/set_authinfo_test.go +++ b/cmd/config/set_authinfo_test.go @@ -81,8 +81,9 @@ func initInputConfig(t *testing.T) (given *config.Config, cleanup func(*testing. kubeAuthInfo := kubeconfig.NewAuthInfo() kubeAuthInfo.Username = testUsername kubeAuthInfo.Password = testPassword - given.KubeConfig().AuthInfos[existingUserName] = kubeAuthInfo - given.AuthInfos[existingUserName].SetKubeAuthInfo(kubeAuthInfo) + encodedKAuthInfo := config.EncodeAuthInfo(kubeAuthInfo) + given.KubeConfig().AuthInfos[existingUserName] = encodedKAuthInfo + given.AuthInfos[existingUserName].SetKubeAuthInfo(encodedKAuthInfo) return given, givenCleanup } diff --git a/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-all-credentials.golden b/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-all-credentials.golden index 7c1cd2b08..4d490d594 100644 --- a/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-all-credentials.golden +++ b/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-all-credentials.golden @@ -1,21 +1,14 @@ LocationOfOrigin: "" -client-certificate: dummy_certificate -client-key: dummy_key -password: dummy_password -token: dummy_token -username: dummy_user +client-certificate: AuthInfoBar_certificate +client-key: AuthInfoBar_key +password: AuthInfoBar_password +token: AuthInfoBar_token +username: AuthInfoBar_user LocationOfOrigin: "" -client-certificate: dummy_certificate -client-key: dummy_key -password: dummy_password -token: dummy_token -username: dummy_user - -LocationOfOrigin: "" -client-certificate: dummy_certificate -client-key: dummy_key -password: dummy_password -token: dummy_token -username: dummy_user +client-certificate: AuthInfoBaz_certificate +client-key: AuthInfoBaz_key +password: AuthInfoBaz_password +token: AuthInfoBaz_token +username: AuthInfoBaz_user diff --git a/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-specific-credentials.golden b/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-specific-credentials.golden index c85b2eab1..0c91f1693 100644 --- a/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-specific-credentials.golden +++ b/cmd/config/testdata/TestGetAuthInfoCmdGoldenOutput/get-specific-credentials.golden @@ -1,7 +1,7 @@ LocationOfOrigin: "" -client-certificate: dummy_certificate -client-key: dummy_key -password: dummy_password -token: dummy_token -username: dummy_user +client-certificate: AuthInfoFoo_certificate +client-key: AuthInfoFoo_key +password: AuthInfoFoo_password +token: AuthInfoFoo_token +username: AuthInfoFoo_user diff --git a/pkg/config/authinfo_test.go b/pkg/config/authinfo_test.go index d3df3b317..c93961755 100644 --- a/pkg/config/authinfo_test.go +++ b/pkg/config/authinfo_test.go @@ -29,7 +29,8 @@ func TestGetAuthInfos(t *testing.T) { conf, cleanup := testutil.InitConfig(t) defer cleanup(t) - authinfos := conf.GetAuthInfos() + authinfos, err := conf.GetAuthInfos() + require.NoError(t, err) assert.Len(t, authinfos, 3) } @@ -65,15 +66,17 @@ func TestModifyAuthInfo(t *testing.T) { authinfo := conf.AddAuthInfo(co) co.Username += stringDelta - co.Password += stringDelta - co.ClientCertificate += stringDelta - co.ClientKey += stringDelta - co.Token += stringDelta + co.Password = newPassword + co.ClientCertificate = newCertificate + co.ClientKey = newKey + co.Token = newToken conf.ModifyAuthInfo(authinfo, co) - assert.EqualValues(t, conf.AuthInfos[co.Name].KubeAuthInfo().Username, co.Username) - assert.EqualValues(t, conf.AuthInfos[co.Name].KubeAuthInfo().Password, co.Password) - assert.EqualValues(t, conf.AuthInfos[co.Name].KubeAuthInfo().ClientCertificate, co.ClientCertificate) - assert.EqualValues(t, conf.AuthInfos[co.Name].KubeAuthInfo().ClientKey, co.ClientKey) - assert.EqualValues(t, conf.AuthInfos[co.Name].KubeAuthInfo().Token, co.Token) - assert.EqualValues(t, conf.AuthInfos[co.Name], authinfo) + modifiedAuthinfo, err := conf.GetAuthInfo(co.Name) + assert.NoError(t, err) + assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Username, co.Username) + assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Password, co.Password) + assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().ClientCertificate, co.ClientCertificate) + assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().ClientKey, co.ClientKey) + assert.EqualValues(t, modifiedAuthinfo.KubeAuthInfo().Token, co.Token) + assert.EqualValues(t, modifiedAuthinfo, authinfo) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 15658cfa3..7026eaf65 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -265,7 +265,7 @@ func (c *Config) reconcileAuthInfos() { // Add the reference c.AuthInfos[key] = NewAuthInfo() } - c.AuthInfos[key].SetKubeAuthInfo(authinfo) + c.AuthInfos[key].authInfo = authinfo } // Checking if there is any AuthInfo reference in airship config that does not match // an actual Auth Info struct in kubeconfig @@ -711,12 +711,17 @@ func (c *Config) GetAuthInfo(aiName string) (*AuthInfo, error) { if !exists { return nil, ErrMissingConfig{What: fmt.Sprintf("User credentials with name '%s'", aiName)} } + decodedAuthInfo, err := DecodeAuthInfo(authinfo.authInfo) + if err != nil { + return nil, err + } + authinfo.authInfo = decodedAuthInfo return authinfo, nil } // GetAuthInfos returns a slice containing all the AuthInfos associated with // the Config sorted by name -func (c *Config) GetAuthInfos() []*AuthInfo { +func (c *Config) GetAuthInfos() ([]*AuthInfo, error) { keys := make([]string, 0, len(c.AuthInfos)) for name := range c.AuthInfos { keys = append(keys, name) @@ -725,9 +730,14 @@ func (c *Config) GetAuthInfos() []*AuthInfo { authInfos := make([]*AuthInfo, 0, len(c.AuthInfos)) for _, name := range keys { + decodedAuthInfo, err := DecodeAuthInfo(c.AuthInfos[name].authInfo) + if err != nil { + return []*AuthInfo{}, err + } + c.AuthInfos[name].authInfo = decodedAuthInfo authInfos = append(authInfos, c.AuthInfos[name]) } - return authInfos + return authInfos, nil } // AddAuthInfo creates new AuthInfo with context details updated @@ -738,7 +748,7 @@ func (c *Config) AddAuthInfo(theAuthInfo *AuthInfoOptions) *AuthInfo { c.AuthInfos[theAuthInfo.Name] = nAuthInfo // Create a new KubeConfig AuthInfo object as well authInfo := clientcmdapi.NewAuthInfo() - nAuthInfo.SetKubeAuthInfo(authInfo) + nAuthInfo.authInfo = authInfo c.KubeConfig().AuthInfos[theAuthInfo.Name] = authInfo c.ModifyAuthInfo(nAuthInfo, theAuthInfo) @@ -747,24 +757,24 @@ func (c *Config) AddAuthInfo(theAuthInfo *AuthInfoOptions) *AuthInfo { // ModifyAuthInfo updates the AuthInfo in the Config object func (c *Config) ModifyAuthInfo(authinfo *AuthInfo, theAuthInfo *AuthInfoOptions) { - kubeAuthInfo := authinfo.KubeAuthInfo() + kubeAuthInfo := EncodeAuthInfo(authinfo.KubeAuthInfo()) if kubeAuthInfo == nil { return } if theAuthInfo.ClientCertificate != "" { - kubeAuthInfo.ClientCertificate = theAuthInfo.ClientCertificate + kubeAuthInfo.ClientCertificate = EncodeString(theAuthInfo.ClientCertificate) } if theAuthInfo.Token != "" { - kubeAuthInfo.Token = theAuthInfo.Token + kubeAuthInfo.Token = EncodeString(theAuthInfo.Token) } if theAuthInfo.Username != "" { kubeAuthInfo.Username = theAuthInfo.Username } if theAuthInfo.Password != "" { - kubeAuthInfo.Password = theAuthInfo.Password + kubeAuthInfo.Password = EncodeString(theAuthInfo.Password) } if theAuthInfo.ClientKey != "" { - kubeAuthInfo.ClientKey = theAuthInfo.ClientKey + kubeAuthInfo.ClientKey = EncodeString(theAuthInfo.ClientKey) } } @@ -909,3 +919,44 @@ func (m *ManagementConfiguration) String() string { } return string(yamlData) } + +// DecodeAuthInfo returns authInfo with credentials decoded +func DecodeAuthInfo(authinfo *clientcmdapi.AuthInfo) (*clientcmdapi.AuthInfo, error) { + password := authinfo.Password + decodedPassword, err := DecodeString(password) + if err != nil { + return nil, ErrDecodingCredentials{Given: password} + } + authinfo.Password = decodedPassword + + token := authinfo.Token + decodedToken, err := DecodeString(token) + if err != nil { + return nil, ErrDecodingCredentials{Given: token} + } + authinfo.Token = decodedToken + + clientCert := authinfo.ClientCertificate + decodedClientCertificate, err := DecodeString(clientCert) + if err != nil { + return nil, ErrDecodingCredentials{Given: clientCert} + } + authinfo.ClientCertificate = decodedClientCertificate + + clientKey := authinfo.ClientKey + decodedClientKey, err := DecodeString(clientKey) + if err != nil { + return nil, ErrDecodingCredentials{Given: clientKey} + } + authinfo.ClientKey = decodedClientKey + return authinfo, nil +} + +// EncodeAuthInfo returns authInfo with credentials base64 encoded +func EncodeAuthInfo(authinfo *clientcmdapi.AuthInfo) *clientcmdapi.AuthInfo { + authinfo.Password = EncodeString(authinfo.Password) + authinfo.Token = EncodeString(authinfo.Token) + authinfo.ClientCertificate = EncodeString(authinfo.ClientCertificate) + authinfo.ClientKey = EncodeString(authinfo.ClientKey) + return authinfo +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index c7da04523..e58e9742b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -34,6 +34,10 @@ const ( stringDelta = "_changed" currentContextName = "def_ephemeral" defaultString = "default" + newToken = "dummy_token_changed" + newPassword = "dummy_password_changed" + newCertificate = "dummy_certificate_changed" + newKey = "dummy_key_changed" ) func TestString(t *testing.T) { diff --git a/pkg/config/errors.go b/pkg/config/errors.go index fd6c2c587..1a79dd263 100644 --- a/pkg/config/errors.go +++ b/pkg/config/errors.go @@ -169,3 +169,12 @@ type ErrEmptyContextName struct { func (e ErrEmptyContextName) Error() string { return "Context name must not be empty." } + +// ErrDecodingCredentials returned when the given string cannot be decoded +type ErrDecodingCredentials struct { + Given string +} + +func (e ErrDecodingCredentials) Error() string { + return fmt.Sprintf("Error decoding credentials. String '%s' cannot not be decoded", e.Given) +} diff --git a/pkg/config/testdata/authinfo-string.yaml b/pkg/config/testdata/authinfo-string.yaml index 7169895b9..b3c2f6793 100644 --- a/pkg/config/testdata/authinfo-string.yaml +++ b/pkg/config/testdata/authinfo-string.yaml @@ -1,6 +1,6 @@ LocationOfOrigin: "" -client-certificate: dummy_certificate -client-key: dummy_key -password: dummy_password -token: dummy_token +client-certificate: ZHVtbXlfY2VydGlmaWNhdGU= +client-key: ZHVtbXlfa2V5 +password: ZHVtbXlfcGFzc3dvcmQ= +token: ZHVtbXlfdG9rZW4= username: dummy_username diff --git a/pkg/config/utils.go b/pkg/config/utils.go index 939f05afa..99d927bb9 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -17,6 +17,8 @@ limitations under the License. package config import ( + "encoding/base64" + "opendev.org/airship/airshipctl/pkg/remote/redfish" ) @@ -109,3 +111,18 @@ func NewRepository() *Repository { func NewAuthInfo() *AuthInfo { return &AuthInfo{} } + +// EncodeString returns the base64 encoding of given string +func EncodeString(given string) string { + return base64.StdEncoding.EncodeToString([]byte(given)) +} + +// DecodeString returns the base64 decoded string +// If err decoding, return the given string +func DecodeString(given string) (string, error) { + decoded, err := base64.StdEncoding.DecodeString(given) + if err != nil { + return "", err + } + return string(decoded), nil +} diff --git a/testutil/testconfig.go b/testutil/testconfig.go index 3f7b1eed7..9ded62399 100644 --- a/testutil/testconfig.go +++ b/testutil/testconfig.go @@ -145,7 +145,8 @@ func DummyAuthInfo() *config.AuthInfo { authinfo.ClientCertificate = "dummy_certificate" authinfo.ClientKey = "dummy_key" authinfo.Token = "dummy_token" - a.SetKubeAuthInfo(authinfo) + encodedAuthInfo := config.EncodeAuthInfo(authinfo) + a.SetKubeAuthInfo(encodedAuthInfo) return a } @@ -356,7 +357,7 @@ users: - name: def-user user: username: dummy_username - password: dummy_password + password: ZHVtbXlfcGFzc3dvcmQK - name: k-admin user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=