certificates/authority/provisioner/options.go

123 lines
3.9 KiB
Go

package provisioner
import (
"encoding/json"
"strings"
"github.com/pkg/errors"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose"
)
// 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)
}
// ProvisionerOptions are a collection of custom options that can be added to
// each provisioner.
// nolint:golint
type ProvisionerOptions struct {
// Template contains a X.509 certificate template. It can be a JSON template
// escaped in a string or it can be also encoded in base64.
Template string `json:"template"`
// TemplateFile points to a file containing a X.509 certificate template.
TemplateFile string `json:"templateFile"`
// TemplateData is a JSON object with variables that can be used in custom
// templates.
TemplateData json.RawMessage `json:"templateData"`
}
// HasTemplate returns true if a template is defined in the provisioner options.
func (o *ProvisionerOptions) HasTemplate() bool {
return o != nil && (o.Template != "" || o.TemplateFile != "")
}
// TemplateOptions generates a CertificateOptions with the template and data
// defined in the ProvisionerOptions, the provisioner generated data, and the
// user data provided in the request. If no template has been provided,
// x509util.DefaultLeafTemplate will be used.
func TemplateOptions(o *ProvisionerOptions, data x509util.TemplateData) (CertificateOptions, error) {
return CustomTemplateOptions(o, data, x509util.DefaultLeafTemplate)
}
// CustomTemplateOptions generates a CertificateOptions with the template, data
// defined in the ProvisionerOptions, the provisioner generated data and the
// user data provided in the request. If no template has been provided in the
// ProvisionerOptions, the given template will be used.
func CustomTemplateOptions(o *ProvisionerOptions, data x509util.TemplateData, defaultTemplate string) (CertificateOptions, error) {
if o != nil {
if data == nil {
data = x509util.NewTemplateData()
}
// 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.HasTemplate() {
return []x509util.Option{
x509util.WithTemplate(defaultTemplate, data),
}
}
// Add user provided data.
if len(so.TemplateData) > 0 {
userObject := make(map[string]interface{})
if err := json.Unmarshal(so.TemplateData, &userObject); err != nil {
data.SetUserData(map[string]interface{}{})
} else {
data.SetUserData(userObject)
}
}
// Load a template from a file if Template is not defined.
if o.Template == "" && o.TemplateFile != "" {
return []x509util.Option{
x509util.WithTemplateFile(o.TemplateFile, data),
}
}
// Load a template from the Template fields
// 1. As a JSON in a string.
template := strings.TrimSpace(o.Template)
if strings.HasPrefix(template, "{") {
return []x509util.Option{
x509util.WithTemplate(template, data),
}
}
// 2. As a base64 encoded JSON.
return []x509util.Option{
x509util.WithTemplateBase64(template, data),
}
}), nil
}
// unsafeParseSigned parses the given token and returns all the claims without
// verifying the signature of the token.
func unsafeParseSigned(s string) (map[string]interface{}, error) {
token, err := jose.ParseSigned(s)
if err != nil {
return nil, err
}
claims := make(map[string]interface{})
if err = token.UnsafeClaimsWithoutVerification(&claims); err != nil {
return nil, err
}
return claims, nil
}