diff --git a/cmd/main.go b/cmd/main.go index 8c32e0bee..709cf8564 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -110,7 +110,7 @@ func buildCommands() *cobra.Command { treeManager := tree.NewTreeManager(ritchieHomeDir, repoLister, api.CoreCmds) credSettings := credential.NewSettings(fileManager, dirManager, userHomeDir) autocompleteGen := autocomplete.NewGenerator(treeManager) - credResolver := envcredential.NewResolver(credFinder) + credResolver := envcredential.NewResolver(credFinder, credSetter, inputPassword) envResolvers := make(env.Resolvers) envResolvers[env.Credential] = credResolver tutorialFinder := rtutorial.NewFinder(ritchieHomeDir, fileManager) diff --git a/pkg/credential/finder.go b/pkg/credential/finder.go index 9a2446fd4..d2bfe675c 100644 --- a/pkg/credential/finder.go +++ b/pkg/credential/finder.go @@ -59,7 +59,7 @@ func (f Finder) Find(provider string) (Detail, error) { cb, err := f.file.Read(File(f.homePath, ctx.Current, provider)) if err != nil { errMsg := fmt.Sprintf(errNotFoundTemplate, provider) - return Detail{}, errors.New(prompt.Red(errMsg)) + return Detail{Credential: Credential{}}, errors.New(prompt.Red(errMsg)) } cred := &Detail{} diff --git a/pkg/credential/finder_test.go b/pkg/credential/finder_test.go index be861d917..a9fb628cc 100644 --- a/pkg/credential/finder_test.go +++ b/pkg/credential/finder_test.go @@ -74,7 +74,7 @@ func TestFind(t *testing.T) { provider: "aws", }, out: out{ - cred: Detail{}, + cred: Detail{Credential: Credential{}}, err: errors.New(prompt.Red(fmt.Sprintf(errNotFoundTemplate, "aws"))), }, }, diff --git a/pkg/env/envcredential/env_credential.go b/pkg/env/envcredential/env_credential.go index 7deeec26d..491c42d23 100644 --- a/pkg/env/envcredential/env_credential.go +++ b/pkg/env/envcredential/env_credential.go @@ -17,7 +17,6 @@ package envcredential import ( - "errors" "fmt" "strings" @@ -27,29 +26,47 @@ import ( type CredentialResolver struct { credential.Finder + credential.Setter + prompt.InputPassword } -const errKeyNotFoundTemplate = `Provider %s has not the credential type %s. -Please verify formula's config.json` - // NewResolver creates a credential resolver instance of Resolver interface -func NewResolver(cf credential.Finder) CredentialResolver { - return CredentialResolver{cf} +func NewResolver(cf credential.Finder, cs credential.Setter, passwordInput prompt.InputPassword) CredentialResolver { + return CredentialResolver{cf, cs, passwordInput} } func (c CredentialResolver) Resolve(name string) (string, error) { - s := strings.Split(name, "_") - service := strings.ToLower(s[1]) - cred, err := c.Find(service) + s := strings.Split(strings.ToLower(name), "_") + provider := s[1] + key := s[2] + cred, err := c.Find(provider) + if err != nil { + // Provider was never set + cred.Service = provider + return c.PromptCredential(provider, key, cred) + } + credValue, exists := cred.Credential[key] + if !exists { + // Provider exists but the expected key doesn't + return c.PromptCredential(provider, key, cred) + } + + // Provider and key exist + return credValue, nil +} + +func (c CredentialResolver) PromptCredential(provider, key string, credentialDetail credential.Detail) (string, error) { + message := fmt.Sprintf("Provider key not found, please provide a value for %s %s: ", provider, key) + inputVal, err := c.Password(message) if err != nil { return "", err } - k := strings.ToLower(s[2]) - credValue, exist := cred.Credential[k] - if !exist { - errMsg := fmt.Sprintf(errKeyNotFoundTemplate, service, strings.ToUpper(name)) - return "", errors.New(prompt.Red(errMsg)) + credentialDetail.Credential[key] = inputVal + + if err := c.Set(credentialDetail); err != nil { + return "", err } - return credValue, nil + + return inputVal, nil } diff --git a/pkg/env/envcredential/env_credential_test.go b/pkg/env/envcredential/env_credential_test.go new file mode 100644 index 000000000..eeb2fe975 --- /dev/null +++ b/pkg/env/envcredential/env_credential_test.go @@ -0,0 +1,79 @@ +/* + * Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package envcredential + +import ( + "os" + "testing" + + "github.com/ZupIT/ritchie-cli/pkg/credential" + "github.com/ZupIT/ritchie-cli/pkg/rcontext" + "github.com/ZupIT/ritchie-cli/pkg/stream" +) + +func TestCredentialResolver(t *testing.T) { + fileManager := stream.NewFileManager() + tempDirectory := os.TempDir() + contextFinder := rcontext.NewFinder(tempDirectory, fileManager) + credentialSetter := credential.NewSetter(tempDirectory, contextFinder) + credentialFinder := credential.NewFinder(tempDirectory, contextFinder, fileManager) + + defer os.RemoveAll(credential.File(tempDirectory, "", "")) + + var tests = []struct { + name string + credentialField string + output string + }{ + { + name: "Test resolve new provider", + credentialField: "CREDENTIAL_PROVIDER_KEY", + output: "key", + }, + { + name: "Test resolve new key", + credentialField: "CREDENTIAL_PROVIDER_KEY2", + output: "key2", + }, + { + name: "Test resolve existing key", + credentialField: "CREDENTIAL_PROVIDER_KEY2", + output: "key2", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + credentialResolver := NewResolver(credentialFinder, credentialSetter, passwordMock{tt.output}) + credentialValue, err := credentialResolver.Resolve(tt.credentialField) + if err != nil { + t.Errorf("Resolve credentials error = %v", err) + } + if credentialValue != tt.output { + t.Errorf("Resolve credentials failed to retrieve. Expected %v, got %v", tt.output, credentialValue) + } + }) + } +} + +type passwordMock struct { + value string +} + +func (pass passwordMock) Password(string) (string, error) { + return pass.value, nil +}