forked from TrueCloudLab/certificates
Use CertificateRequest type as input for ssh NewCertificate.
SSH does not have a real concept of ssh certificate request, but we are using the type to encapsulate the parameters coming in the request.
This commit is contained in:
parent
ad28f0f59a
commit
df1f7e5a2e
7 changed files with 118 additions and 72 deletions
|
@ -29,8 +29,8 @@ type Certificate struct {
|
||||||
|
|
||||||
// NewCertificate creates a new certificate with the given key after parsing a
|
// NewCertificate creates a new certificate with the given key after parsing a
|
||||||
// template given in the options.
|
// template given in the options.
|
||||||
func NewCertificate(key ssh.PublicKey, opts ...Option) (*Certificate, error) {
|
func NewCertificate(cr CertificateRequest, opts ...Option) (*Certificate, error) {
|
||||||
o, err := new(Options).apply(key, opts)
|
o, err := new(Options).apply(cr, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func NewCertificate(key ssh.PublicKey, opts ...Option) (*Certificate, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete with public key
|
// Complete with public key
|
||||||
cert.Key = key
|
cert.Key = cr.Key
|
||||||
|
|
||||||
return &cert, nil
|
return &cert, nil
|
||||||
}
|
}
|
||||||
|
|
17
sshutil/certificate_request.go
Normal file
17
sshutil/certificate_request.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package sshutil
|
||||||
|
|
||||||
|
import "golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
// CertificateRequests simulates a certificate request for SSH. SSH does not
|
||||||
|
// have a concept of certificate requests, but the CA accepts the key and some
|
||||||
|
// other parameters in the requests that are part of the certificate. This
|
||||||
|
// struct will hold these parameters.
|
||||||
|
//
|
||||||
|
// CertificateRequests object will be used in the templates to set parameters
|
||||||
|
// passed with the API instead of the validated ones.
|
||||||
|
type CertificateRequest struct {
|
||||||
|
Key ssh.PublicKey
|
||||||
|
Type CertType
|
||||||
|
KeyID string
|
||||||
|
Principals []string
|
||||||
|
}
|
|
@ -36,9 +36,12 @@ func mustGeneratePublicKey(t *testing.T) ssh.PublicKey {
|
||||||
|
|
||||||
func TestNewCertificate(t *testing.T) {
|
func TestNewCertificate(t *testing.T) {
|
||||||
key := mustGeneratePublicKey(t)
|
key := mustGeneratePublicKey(t)
|
||||||
|
cr := CertificateRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
key ssh.PublicKey
|
cr CertificateRequest
|
||||||
opts []Option
|
opts []Option
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -47,7 +50,7 @@ func TestNewCertificate(t *testing.T) {
|
||||||
want *Certificate
|
want *Certificate
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"user", args{key, []Option{WithTemplate(DefaultCertificate, CreateTemplateData(UserCert, "jane@doe.com", []string{"jane"}))}}, &Certificate{
|
{"user", args{cr, []Option{WithTemplate(DefaultCertificate, CreateTemplateData(UserCert, "jane@doe.com", []string{"jane"}))}}, &Certificate{
|
||||||
Nonce: nil,
|
Nonce: nil,
|
||||||
Key: key,
|
Key: key,
|
||||||
Serial: 0,
|
Serial: 0,
|
||||||
|
@ -68,7 +71,7 @@ func TestNewCertificate(t *testing.T) {
|
||||||
SignatureKey: nil,
|
SignatureKey: nil,
|
||||||
Signature: nil,
|
Signature: nil,
|
||||||
}, false},
|
}, false},
|
||||||
{"host", args{key, []Option{WithTemplate(DefaultCertificate, CreateTemplateData(HostCert, "foobar", []string{"foo.internal", "bar.internal"}))}}, &Certificate{
|
{"host", args{cr, []Option{WithTemplate(DefaultCertificate, CreateTemplateData(HostCert, "foobar", []string{"foo.internal", "bar.internal"}))}}, &Certificate{
|
||||||
Nonce: nil,
|
Nonce: nil,
|
||||||
Key: key,
|
Key: key,
|
||||||
Serial: 0,
|
Serial: 0,
|
||||||
|
@ -83,12 +86,12 @@ func TestNewCertificate(t *testing.T) {
|
||||||
SignatureKey: nil,
|
SignatureKey: nil,
|
||||||
Signature: nil,
|
Signature: nil,
|
||||||
}, false},
|
}, false},
|
||||||
{"file", args{key, []Option{WithTemplateFile("./testdata/github.tpl", TemplateData{
|
{"file", args{cr, []Option{WithTemplateFile("./testdata/github.tpl", TemplateData{
|
||||||
TypeKey: UserCert,
|
TypeKey: UserCert,
|
||||||
KeyIDKey: "john@doe.com",
|
KeyIDKey: "john@doe.com",
|
||||||
PrincipalsKey: []string{"john", "john@doe.com"},
|
PrincipalsKey: []string{"john", "john@doe.com"},
|
||||||
ExtensionsKey: DefaultExtensions(UserCert),
|
ExtensionsKey: DefaultExtensions(UserCert),
|
||||||
InsecureKey: map[string]interface{}{
|
InsecureKey: TemplateData{
|
||||||
"User": map[string]interface{}{"username": "john"},
|
"User": map[string]interface{}{"username": "john"},
|
||||||
},
|
},
|
||||||
})}}, &Certificate{
|
})}}, &Certificate{
|
||||||
|
@ -102,18 +105,18 @@ func TestNewCertificate(t *testing.T) {
|
||||||
ValidBefore: 0,
|
ValidBefore: 0,
|
||||||
CriticalOptions: nil,
|
CriticalOptions: nil,
|
||||||
Extensions: map[string]string{
|
Extensions: map[string]string{
|
||||||
|
"login@github.com": "john",
|
||||||
"permit-X11-forwarding": "",
|
"permit-X11-forwarding": "",
|
||||||
"permit-agent-forwarding": "",
|
"permit-agent-forwarding": "",
|
||||||
"permit-port-forwarding": "",
|
"permit-port-forwarding": "",
|
||||||
"permit-pty": "",
|
"permit-pty": "",
|
||||||
"permit-user-rc": "",
|
"permit-user-rc": "",
|
||||||
"login@github.com": "john",
|
|
||||||
},
|
},
|
||||||
Reserved: nil,
|
Reserved: nil,
|
||||||
SignatureKey: nil,
|
SignatureKey: nil,
|
||||||
Signature: nil,
|
Signature: nil,
|
||||||
}, false},
|
}, false},
|
||||||
{"base64", args{key, []Option{WithTemplateBase64(base64.StdEncoding.EncodeToString([]byte(DefaultCertificate)), CreateTemplateData(HostCert, "foo.internal", nil))}}, &Certificate{
|
{"base64", args{cr, []Option{WithTemplateBase64(base64.StdEncoding.EncodeToString([]byte(DefaultCertificate)), CreateTemplateData(HostCert, "foo.internal", nil))}}, &Certificate{
|
||||||
Nonce: nil,
|
Nonce: nil,
|
||||||
Key: key,
|
Key: key,
|
||||||
Serial: 0,
|
Serial: 0,
|
||||||
|
@ -128,22 +131,22 @@ func TestNewCertificate(t *testing.T) {
|
||||||
SignatureKey: nil,
|
SignatureKey: nil,
|
||||||
Signature: nil,
|
Signature: nil,
|
||||||
}, false},
|
}, false},
|
||||||
{"failNilOptions", args{key, nil}, nil, true},
|
{"failNilOptions", args{cr, nil}, nil, true},
|
||||||
{"failEmptyOptions", args{key, nil}, nil, true},
|
{"failEmptyOptions", args{cr, nil}, nil, true},
|
||||||
{"badBase64Template", args{key, []Option{WithTemplateBase64("foobar", TemplateData{})}}, nil, true},
|
{"badBase64Template", args{cr, []Option{WithTemplateBase64("foobar", TemplateData{})}}, nil, true},
|
||||||
{"badFileTemplate", args{key, []Option{WithTemplateFile("./testdata/missing.tpl", TemplateData{})}}, nil, true},
|
{"badFileTemplate", args{cr, []Option{WithTemplateFile("./testdata/missing.tpl", TemplateData{})}}, nil, true},
|
||||||
{"badJsonTemplate", args{key, []Option{WithTemplate(`{"type":{{ .Type }}}`, TemplateData{})}}, nil, true},
|
{"badJsonTemplate", args{cr, []Option{WithTemplate(`{"type":{{ .Type }}}`, TemplateData{})}}, nil, true},
|
||||||
{"failTemplate", args{key, []Option{WithTemplate(`{{ fail "an error" }}`, TemplateData{})}}, nil, true},
|
{"failTemplate", args{cr, []Option{WithTemplate(`{{ fail "an error" }}`, TemplateData{})}}, nil, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := NewCertificate(tt.args.key, tt.args.opts...)
|
got, err := NewCertificate(tt.args.cr, tt.args.opts...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("NewCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("NewCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("NewCertificate() = %v, want %v", got, tt.want)
|
t.Errorf("NewCertificate() = \n%+v, want \n%+v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/cli/config"
|
"github.com/smallstep/cli/config"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getFuncMap(failMessage *string) template.FuncMap {
|
func getFuncMap(failMessage *string) template.FuncMap {
|
||||||
|
@ -26,9 +25,9 @@ type Options struct {
|
||||||
CertBuffer *bytes.Buffer
|
CertBuffer *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) apply(key ssh.PublicKey, opts []Option) (*Options, error) {
|
func (o *Options) apply(cr CertificateRequest, opts []Option) (*Options, error) {
|
||||||
for _, fn := range opts {
|
for _, fn := range opts {
|
||||||
if err := fn(key, o); err != nil {
|
if err := fn(cr, o); err != nil {
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,12 +35,12 @@ func (o *Options) apply(key ssh.PublicKey, opts []Option) (*Options, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is the type used as a variadic argument in NewCertificate.
|
// Option is the type used as a variadic argument in NewCertificate.
|
||||||
type Option func(key ssh.PublicKey, o *Options) error
|
type Option func(cr CertificateRequest, o *Options) error
|
||||||
|
|
||||||
// WithTemplate is an options that executes the given template text with the
|
// WithTemplate is an options that executes the given template text with the
|
||||||
// given data.
|
// given data.
|
||||||
func WithTemplate(text string, data TemplateData) Option {
|
func WithTemplate(text string, data TemplateData) Option {
|
||||||
return func(key ssh.PublicKey, o *Options) error {
|
return func(cr CertificateRequest, o *Options) error {
|
||||||
terr := new(TemplateError)
|
terr := new(TemplateError)
|
||||||
funcMap := getFuncMap(&terr.Message)
|
funcMap := getFuncMap(&terr.Message)
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ func WithTemplate(text string, data TemplateData) Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
data.SetPublicKey(key)
|
data.SetCertificateRequest(cr)
|
||||||
if err := tmpl.Execute(buf, data); err != nil {
|
if err := tmpl.Execute(buf, data); err != nil {
|
||||||
if terr.Message != "" {
|
if terr.Message != "" {
|
||||||
return terr
|
return terr
|
||||||
|
@ -66,26 +65,26 @@ func WithTemplate(text string, data TemplateData) Option {
|
||||||
// WithTemplateBase64 is an options that executes the given template base64
|
// WithTemplateBase64 is an options that executes the given template base64
|
||||||
// string with the given data.
|
// string with the given data.
|
||||||
func WithTemplateBase64(s string, data TemplateData) Option {
|
func WithTemplateBase64(s string, data TemplateData) Option {
|
||||||
return func(key ssh.PublicKey, o *Options) error {
|
return func(cr CertificateRequest, o *Options) error {
|
||||||
b, err := base64.StdEncoding.DecodeString(s)
|
b, err := base64.StdEncoding.DecodeString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error decoding template")
|
return errors.Wrap(err, "error decoding template")
|
||||||
}
|
}
|
||||||
fn := WithTemplate(string(b), data)
|
fn := WithTemplate(string(b), data)
|
||||||
return fn(key, o)
|
return fn(cr, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTemplateFile is an options that reads the template file and executes it
|
// WithTemplateFile is an options that reads the template file and executes it
|
||||||
// with the given data.
|
// with the given data.
|
||||||
func WithTemplateFile(path string, data TemplateData) Option {
|
func WithTemplateFile(path string, data TemplateData) Option {
|
||||||
return func(key ssh.PublicKey, o *Options) error {
|
return func(cr CertificateRequest, o *Options) error {
|
||||||
filename := config.StepAbs(path)
|
filename := config.StepAbs(path)
|
||||||
b, err := ioutil.ReadFile(filename)
|
b, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error reading %s", path)
|
return errors.Wrapf(err, "error reading %s", path)
|
||||||
}
|
}
|
||||||
fn := WithTemplate(string(b), data)
|
fn := WithTemplate(string(b), data)
|
||||||
return fn(key, o)
|
return fn(cr, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getFuncMap_fail(t *testing.T) {
|
func Test_getFuncMap_fail(t *testing.T) {
|
||||||
|
@ -28,11 +27,14 @@ func Test_getFuncMap_fail(t *testing.T) {
|
||||||
|
|
||||||
func TestWithTemplate(t *testing.T) {
|
func TestWithTemplate(t *testing.T) {
|
||||||
key := mustGeneratePublicKey(t)
|
key := mustGeneratePublicKey(t)
|
||||||
|
cr := CertificateRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
text string
|
text string
|
||||||
data TemplateData
|
data TemplateData
|
||||||
key ssh.PublicKey
|
cr CertificateRequest
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -45,7 +47,7 @@ func TestWithTemplate(t *testing.T) {
|
||||||
KeyIDKey: "jane@doe.com",
|
KeyIDKey: "jane@doe.com",
|
||||||
PrincipalsKey: []string{"jane", "jane@doe.com"},
|
PrincipalsKey: []string{"jane", "jane@doe.com"},
|
||||||
ExtensionsKey: DefaultExtensions(UserCert),
|
ExtensionsKey: DefaultExtensions(UserCert),
|
||||||
}, key}, Options{
|
}, cr}, Options{
|
||||||
CertBuffer: bytes.NewBufferString(`{
|
CertBuffer: bytes.NewBufferString(`{
|
||||||
"type": "user",
|
"type": "user",
|
||||||
"keyId": "jane@doe.com",
|
"keyId": "jane@doe.com",
|
||||||
|
@ -56,24 +58,24 @@ func TestWithTemplate(t *testing.T) {
|
||||||
TypeKey: "host",
|
TypeKey: "host",
|
||||||
KeyIDKey: "foo",
|
KeyIDKey: "foo",
|
||||||
PrincipalsKey: []string{"foo.internal"},
|
PrincipalsKey: []string{"foo.internal"},
|
||||||
}, key}, Options{
|
}, cr}, Options{
|
||||||
CertBuffer: bytes.NewBufferString(`{
|
CertBuffer: bytes.NewBufferString(`{
|
||||||
"type": "host",
|
"type": "host",
|
||||||
"keyId": "foo",
|
"keyId": "foo",
|
||||||
"principals": ["foo.internal"],
|
"principals": ["foo.internal"],
|
||||||
"extensions": null
|
"extensions": null
|
||||||
}`)}, false},
|
}`)}, false},
|
||||||
{"fail", args{`{{ fail "a message" }}`, TemplateData{}, key}, Options{}, true},
|
{"fail", args{`{{ fail "a message" }}`, TemplateData{}, cr}, Options{}, true},
|
||||||
{"failTemplate", args{`{{ fail "fatal error }}`, TemplateData{}, key}, Options{}, true},
|
{"failTemplate", args{`{{ fail "fatal error }}`, TemplateData{}, cr}, Options{}, true},
|
||||||
{"error", args{`{{ mustHas 3 .Data }}`, TemplateData{
|
{"error", args{`{{ mustHas 3 .Data }}`, TemplateData{
|
||||||
"Data": 3,
|
"Data": 3,
|
||||||
}, key}, Options{}, true},
|
}, cr}, Options{}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
var got Options
|
var got Options
|
||||||
fn := WithTemplate(tt.args.text, tt.args.data)
|
fn := WithTemplate(tt.args.text, tt.args.data)
|
||||||
if err := fn(tt.args.key, &got); (err != nil) != tt.wantErr {
|
if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("WithTemplate() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("WithTemplate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
@ -85,11 +87,14 @@ func TestWithTemplate(t *testing.T) {
|
||||||
|
|
||||||
func TestWithTemplateBase64(t *testing.T) {
|
func TestWithTemplateBase64(t *testing.T) {
|
||||||
key := mustGeneratePublicKey(t)
|
key := mustGeneratePublicKey(t)
|
||||||
|
cr := CertificateRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
s string
|
s string
|
||||||
data TemplateData
|
data TemplateData
|
||||||
key ssh.PublicKey
|
cr CertificateRequest
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -102,20 +107,20 @@ func TestWithTemplateBase64(t *testing.T) {
|
||||||
KeyIDKey: "foo.internal",
|
KeyIDKey: "foo.internal",
|
||||||
PrincipalsKey: []string{"foo.internal", "bar.internal"},
|
PrincipalsKey: []string{"foo.internal", "bar.internal"},
|
||||||
ExtensionsKey: map[string]interface{}{"foo": "bar"},
|
ExtensionsKey: map[string]interface{}{"foo": "bar"},
|
||||||
}, key}, Options{
|
}, cr}, Options{
|
||||||
CertBuffer: bytes.NewBufferString(`{
|
CertBuffer: bytes.NewBufferString(`{
|
||||||
"type": "host",
|
"type": "host",
|
||||||
"keyId": "foo.internal",
|
"keyId": "foo.internal",
|
||||||
"principals": ["foo.internal","bar.internal"],
|
"principals": ["foo.internal","bar.internal"],
|
||||||
"extensions": {"foo":"bar"}
|
"extensions": {"foo":"bar"}
|
||||||
}`)}, false},
|
}`)}, false},
|
||||||
{"badBase64", args{"foobar", TemplateData{}, key}, Options{}, true},
|
{"badBase64", args{"foobar", TemplateData{}, cr}, Options{}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
var got Options
|
var got Options
|
||||||
fn := WithTemplateBase64(tt.args.s, tt.args.data)
|
fn := WithTemplateBase64(tt.args.s, tt.args.data)
|
||||||
if err := fn(tt.args.key, &got); (err != nil) != tt.wantErr {
|
if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("WithTemplateBase64() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("WithTemplateBase64() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
@ -127,13 +132,16 @@ func TestWithTemplateBase64(t *testing.T) {
|
||||||
|
|
||||||
func TestWithTemplateFile(t *testing.T) {
|
func TestWithTemplateFile(t *testing.T) {
|
||||||
key := mustGeneratePublicKey(t)
|
key := mustGeneratePublicKey(t)
|
||||||
|
cr := CertificateRequest{
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
data := TemplateData{
|
data := TemplateData{
|
||||||
TypeKey: "user",
|
TypeKey: "user",
|
||||||
KeyIDKey: "jane@doe.com",
|
KeyIDKey: "jane@doe.com",
|
||||||
PrincipalsKey: []string{"jane", "jane@doe.com"},
|
PrincipalsKey: []string{"jane", "jane@doe.com"},
|
||||||
ExtensionsKey: DefaultExtensions(UserCert),
|
ExtensionsKey: DefaultExtensions(UserCert),
|
||||||
InsecureKey: map[string]interface{}{
|
InsecureKey: TemplateData{
|
||||||
UserKey: map[string]interface{}{
|
UserKey: map[string]interface{}{
|
||||||
"username": "jane",
|
"username": "jane",
|
||||||
},
|
},
|
||||||
|
@ -143,7 +151,7 @@ func TestWithTemplateFile(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
path string
|
path string
|
||||||
data TemplateData
|
data TemplateData
|
||||||
key ssh.PublicKey
|
cr CertificateRequest
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -151,7 +159,7 @@ func TestWithTemplateFile(t *testing.T) {
|
||||||
want Options
|
want Options
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"github.com", args{"./testdata/github.tpl", data, key}, Options{
|
{"github.com", args{"./testdata/github.tpl", data, cr}, Options{
|
||||||
CertBuffer: bytes.NewBufferString(`{
|
CertBuffer: bytes.NewBufferString(`{
|
||||||
"type": "user",
|
"type": "user",
|
||||||
"keyId": "jane@doe.com",
|
"keyId": "jane@doe.com",
|
||||||
|
@ -159,13 +167,13 @@ func TestWithTemplateFile(t *testing.T) {
|
||||||
"extensions": {"login@github.com":"jane","permit-X11-forwarding":"","permit-agent-forwarding":"","permit-port-forwarding":"","permit-pty":"","permit-user-rc":""}
|
"extensions": {"login@github.com":"jane","permit-X11-forwarding":"","permit-agent-forwarding":"","permit-port-forwarding":"","permit-pty":"","permit-user-rc":""}
|
||||||
}`),
|
}`),
|
||||||
}, false},
|
}, false},
|
||||||
{"missing", args{"./testdata/missing.tpl", data, key}, Options{}, true},
|
{"missing", args{"./testdata/missing.tpl", data, cr}, Options{}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
var got Options
|
var got Options
|
||||||
fn := WithTemplateFile(tt.args.path, tt.args.data)
|
fn := WithTemplateFile(tt.args.path, tt.args.data)
|
||||||
if err := fn(tt.args.key, &got); (err != nil) != tt.wantErr {
|
if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("WithTemplateFile() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("WithTemplateFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package sshutil
|
package sshutil
|
||||||
|
|
||||||
import "golang.org/x/crypto/ssh"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeKey = "Type"
|
TypeKey = "Type"
|
||||||
KeyIDKey = "KeyID"
|
KeyIDKey = "KeyID"
|
||||||
|
@ -10,7 +8,7 @@ const (
|
||||||
TokenKey = "Token"
|
TokenKey = "Token"
|
||||||
InsecureKey = "Insecure"
|
InsecureKey = "Insecure"
|
||||||
UserKey = "User"
|
UserKey = "User"
|
||||||
PublicKey = "PublicKey"
|
CertificateRequestKey = "CR"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemplateError represents an error in a template produced by the fail
|
// TemplateError represents an error in a template produced by the fail
|
||||||
|
@ -117,10 +115,10 @@ func (t TemplateData) SetUserData(v interface{}) {
|
||||||
t.SetInsecure(UserKey, v)
|
t.SetInsecure(UserKey, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUserData sets the given user provided object in the insecure template
|
// SetCertificateRequest sets the simulated ssh certificate request the insecure
|
||||||
// data.
|
// template data.
|
||||||
func (t TemplateData) SetPublicKey(v ssh.PublicKey) {
|
func (t TemplateData) SetCertificateRequest(cr CertificateRequest) {
|
||||||
t.Set(PublicKey, v)
|
t.SetInsecure(CertificateRequestKey, cr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultCertificate is the default template for an SSH certificate.
|
// DefaultCertificate is the default template for an SSH certificate.
|
||||||
|
@ -130,3 +128,18 @@ const DefaultCertificate = `{
|
||||||
"principals": {{ toJson .Principals }},
|
"principals": {{ toJson .Principals }},
|
||||||
"extensions": {{ toJson .Extensions }}
|
"extensions": {{ toJson .Extensions }}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
const DefaultIIDCertificate = `{
|
||||||
|
"type": "{{ .Type }}",
|
||||||
|
{{- if .Insecure.CR.KeyID }}
|
||||||
|
"keyId": "{{ .Insecure.CR.KeyID }}",
|
||||||
|
{{- else }}
|
||||||
|
"keyId": "{{ .KeyID }}",
|
||||||
|
{{- end}}
|
||||||
|
{{- if .Insecure.CR.Principals }}
|
||||||
|
"principals": {{ toJson .Insecure.CR.Principals }},
|
||||||
|
{{- else }}
|
||||||
|
"principals": {{ toJson .Principals }},
|
||||||
|
{{- end }}
|
||||||
|
"extensions": {{ toJson .Extensions }}
|
||||||
|
}`
|
||||||
|
|
|
@ -3,8 +3,6 @@ package sshutil
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTemplateError_Error(t *testing.T) {
|
func TestTemplateError_Error(t *testing.T) {
|
||||||
|
@ -374,11 +372,11 @@ func TestTemplateData_SetUserData(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateData_SetPublicKey(t *testing.T) {
|
func TestTemplateData_SetCertificateRequest(t *testing.T) {
|
||||||
k1 := mustGeneratePublicKey(t)
|
cr1 := CertificateRequest{Key: mustGeneratePublicKey(t)}
|
||||||
k2 := mustGeneratePublicKey(t)
|
cr2 := CertificateRequest{Key: mustGeneratePublicKey(t)}
|
||||||
type args struct {
|
type args struct {
|
||||||
v ssh.PublicKey
|
cr CertificateRequest
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -386,20 +384,28 @@ func TestTemplateData_SetPublicKey(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
want TemplateData
|
want TemplateData
|
||||||
}{
|
}{
|
||||||
{"ok", TemplateData{}, args{k1}, TemplateData{
|
{"ok", TemplateData{}, args{cr1}, TemplateData{
|
||||||
PublicKey: k1,
|
InsecureKey: TemplateData{
|
||||||
|
CertificateRequestKey: cr1,
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
{"overwrite", TemplateData{
|
{"overwrite", TemplateData{
|
||||||
PublicKey: k1,
|
InsecureKey: TemplateData{
|
||||||
}, args{k2}, TemplateData{
|
UserKey: "data",
|
||||||
PublicKey: k2,
|
CertificateRequestKey: cr1,
|
||||||
|
},
|
||||||
|
}, args{cr2}, TemplateData{
|
||||||
|
InsecureKey: TemplateData{
|
||||||
|
UserKey: "data",
|
||||||
|
CertificateRequestKey: cr2,
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.t.SetPublicKey(tt.args.v)
|
tt.t.SetCertificateRequest(tt.args.cr)
|
||||||
if !reflect.DeepEqual(tt.t, tt.want) {
|
if !reflect.DeepEqual(tt.t, tt.want) {
|
||||||
t.Errorf("TemplateData.SetPublicKey() = %v, want %v", tt.t, tt.want)
|
t.Errorf("TemplateData.SetCertificateRequest() = %v, want %v", tt.t, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue