Skip to content

Commit

Permalink
Add template flag and make more generic set and set-file usage
Browse files Browse the repository at this point in the history
  • Loading branch information
maraino committed Aug 17, 2022
1 parent ed77956 commit 86eb2d1
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 7 deletions.
29 changes: 22 additions & 7 deletions flags/flags.go
Expand Up @@ -356,16 +356,22 @@ domain name 'certs.example-team.ca.smallstep.com' the value would be 'certs'.`,
Usage: `TLS Server Name Indication that should be sent to request a specific certificate from the server.`,
}

// Template is a cli.Flag used to set the template file to use.
Template = cli.StringFlag{
Name: "template",
Usage: `The certificate template <file>, a JSON representation of the certificate to create.`,
}

// TemplateSet is a cli.Flag used to send key-value pairs to the ca.
TemplateSet = cli.StringSliceFlag{
Name: "set",
Usage: "The <key=value> pair with template data variables to send to the CA. Use the **--set** flag multiple times to add multiple variables.",
Usage: "The <key=value> pair with template data variables. Use the **--set** flag multiple times to add multiple variables.",
}

// TemplateSetFile is a cli.Flag used to send a JSON file to the CA.
TemplateSetFile = cli.StringFlag{
Name: "set-file",
Usage: "The JSON <file> with the template data to send to the CA.",
Usage: "The JSON <file> with the template data variables.",
}

// Identity is a cli.Flag used to be able to define the identity argument in
Expand Down Expand Up @@ -429,6 +435,19 @@ func ParseTimeDuration(ctx *cli.Context) (notBefore, notAfter api.TimeDuration,
// ParseTemplateData parses the set and and set-file flags and returns a json
// message to be used in certificate templates.
func ParseTemplateData(ctx *cli.Context) (json.RawMessage, error) {
data, err := GetTemplateData(ctx)
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, nil
}
return json.Marshal(data)
}

// GetTemplateData parses the set and set-file flags and returns a map to be
// used in certificate templates.
func GetTemplateData(ctx *cli.Context) (map[string]interface{}, error) {
data := make(map[string]interface{})
if path := ctx.String("set-file"); path != "" {
b, err := utils.ReadFile(path)
Expand Down Expand Up @@ -457,11 +476,7 @@ func ParseTemplateData(ctx *cli.Context) (json.RawMessage, error) {
}
}

if len(data) == 0 {
return nil, nil
}

return json.Marshal(data)
return data, nil
}

// ParseCaURL gets and parses the ca-url from the command context.
Expand Down
82 changes: 82 additions & 0 deletions flags/flags_test.go
@@ -1,9 +1,13 @@
package flags

import (
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"reflect"
"testing"

"github.com/smallstep/assert"
Expand Down Expand Up @@ -109,3 +113,81 @@ func Test_parseCaURL(t *testing.T) {
})
}
}

func TestParseTemplateData(t *testing.T) {
tempDir := t.TempDir()
write := func(t *testing.T, data []byte) string {
f, err := os.CreateTemp(tempDir, "parseTemplateData")
if err != nil {
t.Fatal(err)
}
_, err = f.Write(data)
if err1 := f.Close(); err1 != nil && err == nil {
t.Fatal(err1)
}
if err != nil {
t.Fatal(err)
}
return f.Name()
}

type args struct {
setData []string
setFileData []byte
}
tests := []struct {
name string
args args
want json.RawMessage
wantErr bool
}{
{"ok nil", args{nil, nil}, nil, false},
{"ok set", args{[]string{"foo=bar"}, nil}, []byte(`{"foo":"bar"}`), false},
{"ok set empty", args{[]string{"foo="}, nil}, []byte(`{"foo":""}`), false},
{"ok set int", args{[]string{"foo=123"}, nil}, []byte(`{"foo":123}`), false},
{"ok set int string", args{[]string{`foo="123"`}, nil}, []byte(`{"foo":"123"}`), false},
{"ok set object", args{[]string{`foo={"foo":"bar"}`}, nil}, []byte(`{"foo":{"foo":"bar"}}`), false},
{"ok set multiple", args{[]string{"foo=bar", "bar=123", "zar={}"}, nil}, []byte(`{"bar":123,"foo":"bar","zar":{}}`), false},
{"ok set overwrite", args{[]string{"foo=bar1", "foo=bar2"}, nil}, []byte(`{"foo":"bar2"}`), false},
{"ok set-file", args{nil, []byte(`{"foo":"bar","bar":123,"zar":{}}`)}, []byte(`{"bar":123,"foo":"bar","zar":{}}`), false},
{"ok set and set-file", args{[]string{"foo=bar-set", "bar=123"}, []byte(`{"foo":"bar-file","zar":{"foo":"bar"}}`)}, []byte(`{"bar":123,"foo":"bar-set","zar":{"foo":"bar"}}`), false},
{"fail set", args{[]string{"foo"}, nil}, nil, true},
{"fail set-file json", args{nil, []byte(`{"foo":"bar}`)}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &cli.App{}
set := flag.NewFlagSet(t.Name(), 0)

if tt.args.setData != nil {
value := cli.StringSlice(tt.args.setData)
set.Var(&value, "set", "")
}
if tt.args.setFileData != nil {
fileName := write(t, tt.args.setFileData)
set.String("set-file", fileName, "")
}

got, err := ParseTemplateData(cli.NewContext(app, set, nil))
if (err != nil) != tt.wantErr {
t.Errorf("ParseTemplateData() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseTemplateData() = %s, want %s", got, tt.want)
}
})
}
}

func TestParseTemplateData_missing(t *testing.T) {
tempDir := t.TempDir()
app := &cli.App{}
set := flag.NewFlagSet(t.Name(), 0)
set.String("set-file", filepath.Join(tempDir, "missing"), "")

_, err := ParseTemplateData(cli.NewContext(app, set, nil))
if err == nil {
t.Errorf("ParseTemplateData() error = %v, wantErr true", err)
}
}

0 comments on commit 86eb2d1

Please sign in to comment.