package provisioner import ( "bytes" "crypto/x509" "encoding/json" "reflect" "testing" "github.com/smallstep/cli/crypto/pemutil" "go.step.sm/crypto/x509util" ) func parseCertificateRequest(t *testing.T, filename string) *x509.CertificateRequest { t.Helper() v, err := pemutil.Read(filename) if err != nil { t.Fatal(err) } csr, ok := v.(*x509.CertificateRequest) if !ok { t.Fatalf("%s is not a certificate request", filename) } return csr } func TestOptions_GetX509Options(t *testing.T) { type fields struct { o *Options } tests := []struct { name string fields fields want *X509Options }{ {"ok", fields{&Options{X509: &X509Options{Template: "foo"}}}, &X509Options{Template: "foo"}}, {"nil", fields{&Options{}}, nil}, {"nilOptions", fields{nil}, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.fields.o.GetX509Options(); !reflect.DeepEqual(got, tt.want) { t.Errorf("Options.GetX509Options() = %v, want %v", got, tt.want) } }) } } func TestOptions_GetSSHOptions(t *testing.T) { type fields struct { o *Options } tests := []struct { name string fields fields want *SSHOptions }{ {"ok", fields{&Options{SSH: &SSHOptions{Template: "foo"}}}, &SSHOptions{Template: "foo"}}, {"nil", fields{&Options{}}, nil}, {"nilOptions", fields{nil}, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.fields.o.GetSSHOptions(); !reflect.DeepEqual(got, tt.want) { t.Errorf("Options.GetSSHOptions() = %v, want %v", got, tt.want) } }) } } func TestProvisionerX509Options_HasTemplate(t *testing.T) { type fields struct { Template string TemplateFile string TemplateData json.RawMessage } tests := []struct { name string fields fields want bool }{ {"template", fields{Template: "the template"}, true}, {"templateFile", fields{TemplateFile: "the template file"}, true}, {"false", fields{}, false}, {"falseWithTemplateData", fields{TemplateData: []byte(`{"foo":"bar"}`)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { o := &X509Options{ Template: tt.fields.Template, TemplateFile: tt.fields.TemplateFile, TemplateData: tt.fields.TemplateData, } if got := o.HasTemplate(); got != tt.want { t.Errorf("ProvisionerOptions.HasTemplate() = %v, want %v", got, tt.want) } }) } } func TestTemplateOptions(t *testing.T) { csr := parseCertificateRequest(t, "testdata/certs/ecdsa.csr") data := x509util.TemplateData{ x509util.SubjectKey: x509util.Subject{ CommonName: "foobar", }, x509util.SANsKey: []x509util.SubjectAlternativeName{ {Type: "dns", Value: "foo.com"}, }, } type args struct { o *Options data x509util.TemplateData } tests := []struct { name string args args want x509util.Options wantErr bool }{ {"ok", args{nil, data}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": {"commonName":"foobar"}, "sans": [{"type":"dns","value":"foo.com"}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"okCustomTemplate", args{&Options{X509: &X509Options{Template: x509util.DefaultIIDLeafTemplate}}, data}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": {"commonName":"foo"}, "sans": [{"type":"dns","value":"foo.com"}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"fail", args{&Options{X509: &X509Options{TemplateData: []byte(`{"badJSON`)}}, data}, x509util.Options{}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cof, err := TemplateOptions(tt.args.o, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("TemplateOptions() error = %v, wantErr %v", err, tt.wantErr) return } var opts x509util.Options if cof != nil { for _, fn := range cof.Options(SignOptions{}) { if err := fn(csr, &opts); err != nil { t.Errorf("x509util.Options() error = %v", err) return } } } if !reflect.DeepEqual(opts, tt.want) { t.Errorf("x509util.Option = %v, want %v", opts, tt.want) } }) } } func TestCustomTemplateOptions(t *testing.T) { csr := parseCertificateRequest(t, "testdata/certs/ecdsa.csr") csrCertificate := `{"version":0,"subject":{"commonName":"foo"},"dnsNames":["foo"],"emailAddresses":null,"ipAddresses":null,"uris":null,"extensions":[{"id":"2.5.29.17","critical":false,"value":"MAWCA2Zvbw=="}]}` data := x509util.TemplateData{ x509util.SubjectKey: x509util.Subject{ CommonName: "foobar", }, x509util.SANsKey: []x509util.SubjectAlternativeName{ {Type: "dns", Value: "foo.com"}, }, } type args struct { o *Options data x509util.TemplateData defaultTemplate string userOptions SignOptions } tests := []struct { name string args args want x509util.Options wantErr bool }{ {"ok", args{nil, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": {"commonName":"foobar"}, "sans": [{"type":"dns","value":"foo.com"}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"okIID", args{nil, data, x509util.DefaultIIDLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": {"commonName":"foo"}, "sans": [{"type":"dns","value":"foo.com"}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"okNoData", args{&Options{}, nil, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": null, "sans": null, "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"okTemplateData", args{&Options{X509: &X509Options{TemplateData: []byte(`{"foo":"bar"}`)}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{ "subject": {"commonName":"foobar"}, "sans": [{"type":"dns","value":"foo.com"}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["serverAuth", "clientAuth"] }`)}, false}, {"okTemplate", args{&Options{X509: &X509Options{Template: "{{ toJson .Insecure.CR }}"}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(csrCertificate)}, false}, {"okFile", args{&Options{X509: &X509Options{TemplateFile: "./testdata/templates/cr.tpl"}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(csrCertificate)}, false}, {"okBase64", args{&Options{X509: &X509Options{Template: "e3sgdG9Kc29uIC5JbnNlY3VyZS5DUiB9fQ=="}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{ CertBuffer: bytes.NewBufferString(csrCertificate)}, false}, {"okUserOptions", args{&Options{X509: &X509Options{Template: `{"foo": "{{.Insecure.User.foo}}"}`}}, data, x509util.DefaultLeafTemplate, SignOptions{TemplateData: []byte(`{"foo":"bar"}`)}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{"foo": "bar"}`), }, false}, {"okBadUserOptions", args{&Options{X509: &X509Options{Template: `{"foo": "{{.Insecure.User.foo}}"}`}}, data, x509util.DefaultLeafTemplate, SignOptions{TemplateData: []byte(`{"badJSON"}`)}}, x509util.Options{ CertBuffer: bytes.NewBufferString(`{"foo": ""}`), }, false}, {"fail", args{&Options{X509: &X509Options{TemplateData: []byte(`{"badJSON`)}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{}, true}, {"failTemplateData", args{&Options{X509: &X509Options{TemplateData: []byte(`{"badJSON}`)}}, data, x509util.DefaultLeafTemplate, SignOptions{}}, x509util.Options{}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cof, err := CustomTemplateOptions(tt.args.o, tt.args.data, tt.args.defaultTemplate) if (err != nil) != tt.wantErr { t.Errorf("CustomTemplateOptions() error = %v, wantErr %v", err, tt.wantErr) return } var opts x509util.Options if cof != nil { for _, fn := range cof.Options(tt.args.userOptions) { if err := fn(csr, &opts); err != nil { t.Errorf("x509util.Options() error = %v", err) return } } } if !reflect.DeepEqual(opts, tt.want) { t.Errorf("x509util.Option = %v, want %v", opts, tt.want) } }) } } func Test_unsafeParseSigned(t *testing.T) { okToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqYW5lQGRvZS5jb20iLCJpc3MiOiJodHRwczovL2RvZS5jb20iLCJqdGkiOiI4ZmYzMjQ4MS1mZDVmLTRlMmUtOTZkZi05MDhjMTI3Yzg1ZjciLCJpYXQiOjE1OTUzNjAwMjgsImV4cCI6MTU5NTM2MzYyOH0.aid8UuhFucJOFHXaob9zpNtVvhul9ulTGsA52mU6XIw" type args struct { s string } tests := []struct { name string args args want map[string]interface{} wantErr bool }{ {"ok", args{okToken}, map[string]interface{}{ "sub": "jane@doe.com", "iss": "https://doe.com", "jti": "8ff32481-fd5f-4e2e-96df-908c127c85f7", "iat": float64(1595360028), "exp": float64(1595363628), }, false}, {"failToken", args{"foobar"}, nil, true}, {"failPayload", args{"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ew.aid8UuhFucJOFHXaob9zpNtVvhul9ulTGsA52mU6XIw"}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := unsafeParseSigned(tt.args.s) if (err != nil) != tt.wantErr { t.Errorf("unsafeParseSigned() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("unsafeParseSigned() = \n%v, want \n%v", got, tt.want) } }) } }