Skip to content

Commit

Permalink
feat: support for generated secrets in schemas
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 committed Jun 20, 2019
1 parent cb01881 commit b5b8f31
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
30 changes: 25 additions & 5 deletions pkg/surveyutils/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strconv"
"strings"

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

"github.com/pkg/errors"

"github.com/jenkins-x/jx/pkg/kube"
Expand All @@ -21,6 +23,10 @@ import (
"gopkg.in/AlecAivazis/survey.v1/terminal"
)

var (
generatedPasswordValue = "<generated>"
)

// Type represents a JSON Schema object type current to https://www.ietf.org/archive/id/draft-handrews-json-schema-validation-01.txt
type Type struct {
Version string `json:"$schema,omitempty"`
Expand Down Expand Up @@ -664,10 +670,10 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
// Custom format support for passwords
storeAsSecret := false
var err error
if util.DereferenceString(t.Format) == "password" || util.DereferenceString(t.Format) == "token" || util.DereferenceString(t.Format) == "password-passthrough" || util.DereferenceString(t.
Format) == "token-passthrough" {
dereferencedFormat := strings.TrimSuffix(util.DereferenceString(t.Format), "-passthorugh")
if dereferencedFormat == "password" || dereferencedFormat == "token" {
storeAsSecret = true
result, err = handlePasswordProperty(message, help, ask, validator, surveyOpts, defaultValue,
result, err = handlePasswordProperty(message, help, dereferencedFormat, ask, validator, surveyOpts, defaultValue,
autoAcceptMessage, o.Out, t.Type)
if err != nil {
return errors.WithStack(err)
Expand Down Expand Up @@ -767,7 +773,7 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
if err != nil {
return err
}
secretReference, err := o.CreateSecret(secretName, util.DereferenceString(t.Format), value)
secretReference, err := o.CreateSecret(secretName, dereferencedFormat, value)
if err != nil {
return err
}
Expand All @@ -779,7 +785,7 @@ func (o *JSONSchemaOptions) handleBasicProperty(name string, prefixes []string,
return nil
}

func handlePasswordProperty(message string, help string, ask bool, validator survey.Validator,
func handlePasswordProperty(message string, help string, kind string, ask bool, validator survey.Validator,
surveyOpts survey.AskOpt, defaultValue string, autoAcceptMessage string, out terminal.FileWriter,
t string) (interface{}, error) {
// Secret input
Expand All @@ -788,6 +794,17 @@ func handlePasswordProperty(message string, help string, ask bool, validator sur
Help: help,
}

generated := false
if defaultValue == generatedPasswordValue {
secret, err := secrets.DefaultGenerateSecret()
if err != nil {
return nil, errors.WithStack(err)
}
defaultValue = secret
fmt.Fprintf(terminal.NewAnsiStdout(out), "Generated %s %s, to use it press enter.\nThis is the only time you will be shown it so remember to save it\n", kind, util.ColorInfo(secret))
generated = true
}

var answer string
if ask {
err := survey.AskOne(prompt, &answer, validator, surveyOpts)
Expand All @@ -802,6 +819,9 @@ func handlePasswordProperty(message string, help string, ask bool, validator sur
return nil, errors.Wrapf(err, "writing %s to console", msg)
}
}
if answer == "" && generated {
answer = defaultValue
}
if answer != "" {
result, err := convertAnswer(answer, t)
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions pkg/surveyutils/jsonschema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,32 @@ func TestToken(t *testing.T) {
})
}

func TestGeneratedToken(t *testing.T) {
tests.SkipForWindows(t, "go-expect does not work on windows")
tests.Retry(t, 1, time.Second*10, func(r *tests.R) {
values, secrets, err := GenerateValuesAsYaml(r, "generatedToken.test.schema.json", make(map[string]interface{}), false,
false,
false, false,
func(console *tests.ConsoleWrapper, donec chan struct{}) {
defer close(donec)
// Test boolean type
console.ExpectString("Enter a value for tokenValue")
console.SendLine("")
console.ExpectEOF()
})
assert.Equal(r, `tokenValue:
kind: Secret
name: tokenvalue-secret
`, values)
assert.Len(t, secrets, 1)
secret := secrets[0]
assert.Equal(t, "tokenvalue-secret", secret.Name)
assert.Equal(t, "token", secret.Key)
assert.Len(t, secret.Value, 20)
assert.NoError(r, err)
})
}

func TestEmail(t *testing.T) {
tests.SkipForWindows(t, "go-expect does not work on windows")
tests.Retry(t, 5, time.Second*10, func(r *tests.R) {
Expand Down
14 changes: 14 additions & 0 deletions pkg/surveyutils/test_data/generatedToken.test.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

{
"$id": "https:/jenkins-x.io/tests/basicTypes.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "test values.yaml",
"type": "object",
"properties": {
"tokenValue": {
"type": "string",
"format": "token",
"default":"<generated>"
}
}
}
34 changes: 34 additions & 0 deletions pkg/util/secrets/secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package secrets

import (
"github.com/pkg/errors"
"github.com/sethvargo/go-password/password"
)

const (
allowedSymbols = "~!#%^_+-=?,."
length = 20
numDigits = 4
numSymbols = 2
upperCaseAllowed = true
allowRepeat = true
)

// DefaultGenerateSecret generates a secret using sensible defaults
func DefaultGenerateSecret() (string, error) {
input := password.GeneratorInput{
Symbols: allowedSymbols,
}

generator, err := password.NewGenerator(&input)
if err != nil {
return "", errors.Wrap(err, "unable to create password generator")
}

secret, err := generator.Generate(length, numDigits, numSymbols, !upperCaseAllowed, allowRepeat)

if err != nil {
return "", errors.Wrap(err, "unable to generate secret")
}
return secret, nil
}

0 comments on commit b5b8f31

Please sign in to comment.