Skip to content

Commit

Permalink
feat: support for generated secrets in schemas (#4347) (#4360)
Browse files Browse the repository at this point in the history
If `"default":"<generated>”` is used on a token or a password then it will default to a generated secret

Signed-off-by: Pete Muir <pmuir@bleepbleep.org.uk>
  • Loading branch information
pmuir authored and jenkins-x-bot committed Jun 21, 2019
1 parent 921163c commit 7b9c9db
Show file tree
Hide file tree
Showing 5 changed files with 567 additions and 616 deletions.
2 changes: 1 addition & 1 deletion pkg/cmd/step/create/step_create_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var timeout = 5 * time.Second
func TestCreateValuesFileWithVault(t *testing.T) {
tests.SkipForWindows(t, "go-expect does not work on windows")
pegomock.RegisterMockTestingT(t)
tests.Retry(t, 5, time.Second*10, func(r *tests.R) {
tests.Retry(t, 1, time.Second*10, func(r *tests.R) {
testOrgNameUUID, err := uuid.NewV4()
assert.NoError(t, err)
testOrgName := testOrgNameUUID.String()
Expand Down
99 changes: 57 additions & 42 deletions pkg/surveyutils/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"strconv"
"strings"

"github.com/jenkins-x/jx/pkg/log"

"github.com/jenkins-x/jx/pkg/secreturl"

"github.com/jenkins-x/jx/pkg/log"

"github.com/jenkins-x/jx/pkg/util/secrets"

"github.com/pkg/errors"
Expand All @@ -20,7 +20,7 @@ import (

"github.com/iancoleman/orderedmap"

"gopkg.in/AlecAivazis/survey.v1"
survey "gopkg.in/AlecAivazis/survey.v1"

"gopkg.in/AlecAivazis/survey.v1/terminal"
)
Expand Down Expand Up @@ -625,6 +625,16 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
return o.handleConst(name, validators, t, output)
}

// lets use the last vaultPath as the vaultKey. We don't need to use the format
// in the vaultKey or vaultPath
lastIdx := len(prefixes) - 1
vaultKey := prefixes[lastIdx]
pathPrefixes := prefixes[0:lastIdx]
vaultPath := o.VaultBasePath
vaultDir := strings.Join(pathPrefixes, "-")
if vaultDir != "" {
vaultPath = strings.Join([]string{vaultPath, vaultDir}, "/")
}
ask := true
defaultValue := ""
autoAcceptMessage := ""
Expand Down Expand Up @@ -663,16 +673,54 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
validator := survey.ComposeValidators(validators...)
// Ask the question
// Custom format support for passwords
storeAsSecret := false
var err error
dereferencedFormat := strings.TrimSuffix(util.DereferenceString(t.Format), "-passthrough")
if dereferencedFormat == "password" || dereferencedFormat == "token" {
storeAsSecret = true
result, err = handlePasswordProperty(message, help, dereferencedFormat, ask, validator, surveyOpts, defaultValue,
if o.VaultClient != nil {
// the standard existing logic is not used in this case
secret, err := o.VaultClient.Read(vaultPath)
if err == nil {
if value, ok := secret[vaultKey]; ok {
if !o.AskExisting {
ask = false
}
defaultValue = fmt.Sprintf("%v", value)
autoAcceptMessage = "Automatically accepted existing value"
}
} else {
// If there is an error, just continue
log.Logger().Debugf("Error reading %s from vault %v", vaultPath, err)
}
}

secret, err := handlePasswordProperty(message, help, dereferencedFormat, ask, validator, surveyOpts, defaultValue,
autoAcceptMessage, o.Out, t.Type)
if err != nil {
return errors.WithStack(err)
}
if secret != nil {
value, err := util.AsString(secret)
if err != nil {
return err
}
if o.VaultClient != nil {

secretReference := secreturl.ToURI(vaultPath, vaultKey, o.VaultScheme)
output.Set(name, secretReference)

// lets upsert the vaultKey
data, _ := o.VaultClient.Read(vaultPath)
if data == nil {
data = map[string]interface{}{}
}
data[vaultKey] = value
_, err = o.VaultClient.Write(vaultPath, data)
if err != nil {
return errors.Wrapf(err, "failed to write to vaultPath %s with data %#v", vaultPath, data)
}
} else {
log.Logger().Warnf("Need to store a secret for %s but no secret store configured", name)
}
}
} else if t.Enum != nil {
var enumResult string
// Support for selects
Expand Down Expand Up @@ -762,40 +810,7 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
}
}

if storeAsSecret && result != nil {
value, err := util.AsString(result)
if err != nil {
return err
}
if o.VaultClient != nil {
// lets use the last path as the key. We don't need to use the format
// in the key or path
lastIdx := len(prefixes) - 1
key := prefixes[lastIdx]
pathPrefixes := prefixes[0:lastIdx]
path := o.VaultBasePath
dir := strings.Join(pathPrefixes, "-")
if dir != "" {
path = strings.Join([]string{path, dir}, "/")
}
secretReference := secreturl.ToURI(path, key, o.VaultScheme)
output.Set(name, secretReference)

// lets upsert the key
data, _ := o.VaultClient.Read(path)
if data == nil {
data = map[string]interface{}{}
}
data[key] = value
_, err = o.VaultClient.Write(path, data)
if err != nil {
return errors.Wrapf(err, "failed to write to path %s with data %#v", path, data)
}
} else {
log.Logger().Warnf("Need to store a secret for %s but no secret store configured", name)
}

} else if result != nil {
if result != nil {
// Write the value to the output
output.Set(name, result)
}
Expand Down Expand Up @@ -830,7 +845,7 @@ func handlePasswordProperty(message string, help string, kind string, ask bool,
}
} else {
answer = defaultValue
msg := fmt.Sprintf("%s %s [%s]\n", message, util.ColorInfo(answer), autoAcceptMessage)
msg := fmt.Sprintf("%s *** [%s]\n", message, autoAcceptMessage)
_, err := fmt.Fprint(terminal.NewAnsiStdout(out), msg)
if err != nil {
return nil, errors.Wrapf(err, "writing %s to console", msg)
Expand Down
Loading

0 comments on commit 7b9c9db

Please sign in to comment.