diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index cc513dc6..feff49bf 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" ) @@ -25,7 +26,7 @@ type stepPayload struct { // JWK is the default provisioner, an entity that can sign tokens necessary for // signature requests. type JWK struct { - *base + base Type string `json:"type"` Name string `json:"name"` Key *jose.JSONWebKey `json:"key"` @@ -151,7 +152,14 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er claims.SANs = []string{claims.Subject} } + data := x509util.CreateTemplateData(claims.Subject, claims.SANs) + templateOptions, err := TemplateOptions(p.Options, data) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign") + } + return []SignOption{ + templateOptions, // modifiers / withOptions newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID), profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), diff --git a/authority/provisioner/options.go b/authority/provisioner/options.go new file mode 100644 index 00000000..72263466 --- /dev/null +++ b/authority/provisioner/options.go @@ -0,0 +1,72 @@ +package provisioner + +import ( + "encoding/json" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/x509util" +) + +// CertificateOptions is an interface that returns a list of options passed when +// creating a new certificate. +type CertificateOptions interface { + Options(Options) []x509util.Option +} + +type certificateOptionsFunc func(Options) []x509util.Option + +func (fn certificateOptionsFunc) Options(so Options) []x509util.Option { + return fn(so) +} + +type ProvisionerOptions struct { + Template string `json:"template"` + TemplateFile string `json:"templateFile` + TemplateData json.RawMessage `json:"templateData"` +} + +// TemplateOptions generate a CertificateOptions with the template and data +// defined in the ProvisionerOptions, the provisioner generated data, and the +// user data provided in the request. +func TemplateOptions(o *ProvisionerOptions, data x509util.TemplateData) (CertificateOptions, error) { + if o != nil { + if data == nil { + data = make(x509util.TemplateData) + } + + // Add template data if any. + if len(o.TemplateData) > 0 { + if err := json.Unmarshal(o.TemplateData, data); err != nil { + return nil, errors.Wrap(err, "error unmarshaling template data") + } + } + + } + + return certificateOptionsFunc(func(so Options) []x509util.Option { + // We're not provided user data without custom templates. + if o == nil || (o.Template == "" && o.TemplateFile == "") { + return []x509util.Option{ + x509util.WithTemplate(x509util.DefaultLeafTemplate, data), + } + } + + // Add user provided data. + if len(so.UserData) > 0 { + userObject := make(map[string]interface{}) + if err := json.Unmarshal(so.UserData, userObject); err != nil { + data[x509util.UserKey] = map[string]interface{}{} + } else { + data[x509util.UserKey] = userObject + } + } + if o.Template == "" && o.TemplateFile != "" { + return []x509util.Option{ + x509util.WithTemplateFile(o.TemplateFile, data), + } + } + return []x509util.Option{ + x509util.WithTemplateFile(o.Template, data), + } + }), nil +} diff --git a/authority/provisioner/provisioner.go b/authority/provisioner/provisioner.go index 4d8671e7..26ead6ff 100644 --- a/authority/provisioner/provisioner.go +++ b/authority/provisioner/provisioner.go @@ -279,7 +279,9 @@ func SanitizeSSHUserPrincipal(email string) string { }, strings.ToLower(email)) } -type base struct{} +type base struct { + Options *ProvisionerOptions `json:"options"` +} // AuthorizeSign returns an unimplemented error. Provisioners should overwrite // this method if they will support authorizing tokens for signing x509 Certificates. diff --git a/x509util/options.go b/x509util/options.go index 70bab0ff..f82e9de9 100644 --- a/x509util/options.go +++ b/x509util/options.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "text/template" - "github.com/Masterminds/sprig" + "github.com/Masterminds/sprig/v3" "github.com/pkg/errors" "github.com/smallstep/cli/config" )