From 157dee9dc2cbc8b23137d24fa3c1826e84161fb3 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 27 Jul 2024 23:23:49 +0200 Subject: [PATCH] Refactor flag binding functions --- cert.go | 10 +++- cmd/pcert/create.go | 5 +- cmd/pcert/flag_certificate.go | 49 +++++++++++++++++++ cmd/pcert/flag_key.go | 18 +++++++ cmd/pcert/flag_maxpathlength.go | 31 ++++++------ cmd/pcert/flag_request.go | 24 ++++++++++ cmd/pcert/flag_subject.go | 15 ++++++ cmd/pcert/flags.go | 85 --------------------------------- cmd/pcert/request.go | 7 +-- cmd/pcert/sign.go | 3 +- 10 files changed, 136 insertions(+), 111 deletions(-) create mode 100644 cmd/pcert/flag_certificate.go create mode 100644 cmd/pcert/flag_key.go create mode 100644 cmd/pcert/flag_request.go delete mode 100644 cmd/pcert/flags.go diff --git a/cert.go b/cert.go index 6bee540..2f0714e 100644 --- a/cert.go +++ b/cert.go @@ -178,9 +178,15 @@ func NewCertificate(opts *CertificateOptions) *x509.Certificate { Policies: opts.Policies, } - if opts.MaxPathLen != nil { - cert.MaxPathLen = *opts.MaxPathLen + if opts.MaxPathLen == nil { + cert.MaxPathLen = -1 + cert.MaxPathLenZero = false + } else if *opts.MaxPathLen == 0 { + cert.MaxPathLen = 0 cert.MaxPathLenZero = true + } else { + cert.MaxPathLen = *opts.MaxPathLen + cert.MaxPathLenZero = false } if cert.NotBefore.IsZero() { diff --git a/cmd/pcert/create.go b/cmd/pcert/create.go index 6ac2f42..84fc74b 100644 --- a/cmd/pcert/create.go +++ b/cmd/pcert/create.go @@ -182,7 +182,8 @@ pcert create tls.crt cmd.Flags().StringVarP(&createCommand.SignCertificateLocation, "sign-cert", "s", createCommand.SignCertificateLocation, "Certificate used to sign. If not specified a self-signed certificate is created") cmd.Flags().StringVar(&createCommand.SignKeyLocation, "sign-key", createCommand.SignKeyLocation, "Key used to sign. If not specified but --sign-cert is specified we use the key file relative to the certificate specified with --sign-cert.") cmd.Flags().StringSliceVar(&createCommand.Profiles, "profile", createCommand.Profiles, "Certificates profiles to apply (server, client, ca)") - BindCertificateOptionsFlags(cmd.Flags(), &createCommand.CertificateOptions) - BindKeyFlags(cmd.Flags(), &createCommand.KeyOptions) + + registerCertFlags(cmd, &createCommand.CertificateOptions) + registerKeyFlags(cmd, &createCommand.KeyOptions) return cmd } diff --git a/cmd/pcert/flag_certificate.go b/cmd/pcert/flag_certificate.go new file mode 100644 index 0000000..0010f27 --- /dev/null +++ b/cmd/pcert/flag_certificate.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "net" + "time" + + "github.com/dvob/pcert" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func registerCertFlags(cmd *cobra.Command, certOpts *pcert.CertificateOptions) { + bindCertFlags(cmd.Flags(), certOpts) + + _ = cmd.RegisterFlagCompletionFunc("sign-alg", signAlgorithmCompletionFunc) + + _ = cmd.RegisterFlagCompletionFunc("key-usage", keyUsageCompletionFunc) + _ = cmd.RegisterFlagCompletionFunc("ext-key-usage", extKeyUsageCompletionFunc) +} + +func bindCertFlags(fs *pflag.FlagSet, co *pcert.CertificateOptions) { + // SAN + fs.StringSliceVar(&co.DNSNames, "dns", []string{}, "DNS subject alternative name.") + fs.StringSliceVar(&co.EmailAddresses, "email", []string{}, "Email subject alternative name.") + fs.IPSliceVar(&co.IPAddresses, "ip", []net.IP{}, "IP subject alternative name.") + fs.Var(newURISliceValue(&co.URIs), "uri", "URI subject alternative name.") + + // signature algorithm + fs.Var(newSignAlgValue(&co.SignatureAlgorithm), "sign-alg", "Signature Algorithm. See 'pcert list' for available algorithms.") + + // validity duration + fs.Var(newTimeValue(&co.NotBefore), "not-before", fmt.Sprintf("Not valid before time in RFC3339 format (e.g. '%s').", time.Now().UTC().Format(time.RFC3339))) + fs.Var(newTimeValue(&co.NotAfter), "not-after", fmt.Sprintf("Not valid after time in RFC3339 format (e.g. '%s').", time.Now().Add(time.Hour*24*60).UTC().Format(time.RFC3339))) + fs.Var(newDurationValue(&co.Expiry), "expiry", "Validity period of the certificate. If --not-after is set this option has no effect.") + + // subject + fs.Var(newSubjectValue(&co.Subject), "subject", "Subject in the form '/C=CH/O=My Org/OU=My Team'.") + bindSubjectFlags(fs, &co.Subject) + + // basic constraints + fs.BoolVar(&co.BasicConstraintsValid, "basic-constraints", co.BasicConstraintsValid, "Add basic constraints extension.") + fs.BoolVar(&co.IsCA, "is-ca", co.IsCA, "Mark certificate as CA in the basic constraints. Only takes effect if --basic-constraints is true.") + fs.Var(newMaxPathLengthValue(co.MaxPathLen), "max-path-length", "Sets the max path length in the basic constraints.") + + // key usage + fs.Var(newKeyUsageValue(&co.KeyUsage), "key-usage", "Set the key usage. See 'pcert list' for available key usages.") + fs.Var(newExtKeyUsageValue(&co.ExtKeyUsage), "ext-key-usage", "Set the extended key usage. See 'pcert list' for available extended key usages.") +} diff --git a/cmd/pcert/flag_key.go b/cmd/pcert/flag_key.go new file mode 100644 index 0000000..00a598b --- /dev/null +++ b/cmd/pcert/flag_key.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/dvob/pcert" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func registerKeyFlags(cmd *cobra.Command, keyOpts *pcert.KeyOptions) { + bindKeyFlags(cmd.Flags(), keyOpts) + + _ = cmd.RegisterFlagCompletionFunc("key-alg", keyAlgorithmCompletionFunc) +} + +func bindKeyFlags(fs *pflag.FlagSet, keyOptions *pcert.KeyOptions) { + fs.Var(newKeyAlgorithmValue(&keyOptions.Algorithm), "key-alg", "Public key algorithm. See 'pcert list' for available algorithms.") + fs.IntVar(&keyOptions.Size, "key-size", keyOptions.Size, "Key Size. This defaults to 256 for ECDSA and to 2048 for RSA.") +} diff --git a/cmd/pcert/flag_maxpathlength.go b/cmd/pcert/flag_maxpathlength.go index 771ea71..27241e2 100644 --- a/cmd/pcert/flag_maxpathlength.go +++ b/cmd/pcert/flag_maxpathlength.go @@ -1,41 +1,40 @@ package main import ( - "crypto/x509" "strconv" ) type maxPathLengthValue struct { - cert *x509.Certificate + maxPathLen *int } -func newMaxPathLengthValue(c *x509.Certificate) *maxPathLengthValue { +func newMaxPathLengthValue(maxPathLen *int) *maxPathLengthValue { return &maxPathLengthValue{ - cert: c, + maxPathLen: maxPathLen, } } func (m *maxPathLengthValue) Type() string { - return "int|none" + return "int|-" } func (m *maxPathLengthValue) String() string { - if m.cert.MaxPathLen < 0 { - return "none" + if m.maxPathLen == nil { + return "-" } - if m.cert.MaxPathLen == 0 && !m.cert.MaxPathLenZero { - return "none" - } - return strconv.Itoa(m.cert.MaxPathLen) + return strconv.Itoa(*m.maxPathLen) } func (m *maxPathLengthValue) Set(length string) error { var err error - if length == "none" { - m.cert.MaxPathLen = -1 - return nil + if length == "-" { + m.maxPathLen = nil } - m.cert.MaxPathLen, err = strconv.Atoi(length) - return err + value, err := strconv.Atoi(length) + if err != nil { + return err + } + m.maxPathLen = &value + return nil } diff --git a/cmd/pcert/flag_request.go b/cmd/pcert/flag_request.go new file mode 100644 index 0000000..c83209b --- /dev/null +++ b/cmd/pcert/flag_request.go @@ -0,0 +1,24 @@ +package main + +import ( + "crypto/x509" + "net" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func registerRequestFlags(cmd *cobra.Command, csr *x509.CertificateRequest) { + bindRequestFlags(cmd.Flags(), csr) + + _ = cmd.RegisterFlagCompletionFunc("sign-alg", signAlgorithmCompletionFunc) +} + +func bindRequestFlags(fs *pflag.FlagSet, csr *x509.CertificateRequest) { + fs.StringSliceVar(&csr.DNSNames, "dns", []string{}, "DNS subject alternative name.") + fs.StringSliceVar(&csr.EmailAddresses, "email", []string{}, "Email subject alternative name.") + fs.IPSliceVar(&csr.IPAddresses, "ip", []net.IP{}, "IP subject alternative name.") + fs.Var(newURISliceValue(&csr.URIs), "uri", "URI subject alternative name.") + fs.Var(newSignAlgValue(&csr.SignatureAlgorithm), "sign-alg", "Signature Algorithm. See 'pcert list' for available algorithms.") + fs.Var(newSubjectValue(&csr.Subject), "subject", "Subject in the form '/C=CH/O=My Org/OU=My Team'.") +} diff --git a/cmd/pcert/flag_subject.go b/cmd/pcert/flag_subject.go index 72500ae..b2cb6f8 100644 --- a/cmd/pcert/flag_subject.go +++ b/cmd/pcert/flag_subject.go @@ -4,8 +4,23 @@ import ( "crypto/x509/pkix" "fmt" "strings" + + "github.com/spf13/pflag" ) +func bindSubjectFlags(fs *pflag.FlagSet, subject *pkix.Name) { + fs.StringSliceVar(&subject.Country, "subject-country", subject.Country, "subject country (C)") + fs.StringSliceVar(&subject.Organization, "subject-org", subject.Organization, "subject organization (O)") + fs.StringSliceVar(&subject.OrganizationalUnit, "subject-ou", subject.OrganizationalUnit, "subject organizational unit (OU)") + fs.StringSliceVar(&subject.Locality, "subject-locality", subject.Locality, "subject locality (L)") + fs.StringSliceVar(&subject.Province, "subject-province", subject.Province, "subject province (P)") + fs.StringSliceVar(&subject.StreetAddress, "subject-street-address", subject.StreetAddress, "subject street address (STREET)") + fs.StringSliceVar(&subject.PostalCode, "subject-postal-code", subject.PostalCode, "subject postal code (POSTALCODE)") + fs.StringVar(&subject.SerialNumber, "subject-serial-number", subject.SerialNumber, "subject serial number (SERIALNUMBER)") + fs.StringVar(&subject.CommonName, "subject-common-name", subject.CommonName, "subject common name (CN)") + fs.StringVarP(&subject.CommonName, "name", "n", subject.CommonName, "subject common name (CN). alias for --subject-common-name") +} + type subjectValue struct { value *pkix.Name } diff --git a/cmd/pcert/flags.go b/cmd/pcert/flags.go deleted file mode 100644 index b7541a9..0000000 --- a/cmd/pcert/flags.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "crypto/x509" - "fmt" - "net" - "time" - - "github.com/dvob/pcert" - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -// BindCertificateRequestFlags binds flags to a x509.CertificateRequest -func BindCertificateRequestFlags(fs *pflag.FlagSet, csr *x509.CertificateRequest) { - fs.StringSliceVar(&csr.DNSNames, "dns", []string{}, "DNS subject alternative name.") - fs.StringSliceVar(&csr.EmailAddresses, "email", []string{}, "Email subject alternative name.") - fs.IPSliceVar(&csr.IPAddresses, "ip", []net.IP{}, "IP subject alternative name.") - fs.Var(newURISliceValue(&csr.URIs), "uri", "URI subject alternative name.") - fs.Var(newSignAlgValue(&csr.SignatureAlgorithm), "sign-alg", "Signature Algorithm. See 'pcert list' for available algorithms.") - fs.Var(newSubjectValue(&csr.Subject), "subject", "Subject in the form '/C=CH/O=My Org/OU=My Team'.") -} - -// BindCertificateFlags binds flags to a x509.Certificate -func BindCertificateFlags(fs *pflag.FlagSet, cert *x509.Certificate) { - fs.StringSliceVar(&cert.DNSNames, "dns", []string{}, "DNS subject alternative name.") - fs.StringSliceVar(&cert.EmailAddresses, "email", []string{}, "Email subject alternative name.") - fs.IPSliceVar(&cert.IPAddresses, "ip", []net.IP{}, "IP subject alternative name.") - fs.Var(newURISliceValue(&cert.URIs), "uri", "URI subject alternative name.") - fs.Var(newSignAlgValue(&cert.SignatureAlgorithm), "sign-alg", "Signature Algorithm. See 'pcert list' for available algorithms.") - fs.Var(newTimeValue(&cert.NotBefore), "not-before", fmt.Sprintf("Not valid before time in RFC3339 format (e.g. '%s').", time.Now().UTC().Format(time.RFC3339))) - fs.Var(newTimeValue(&cert.NotAfter), "not-after", fmt.Sprintf("Not valid after time in RFC3339 format (e.g. '%s').", time.Now().Add(time.Hour*24*60).UTC().Format(time.RFC3339))) - fs.Var(newSubjectValue(&cert.Subject), "subject", "Subject in the form '/C=CH/O=My Org/OU=My Team'.") - - fs.BoolVar(&cert.BasicConstraintsValid, "basic-constraints", cert.BasicConstraintsValid, "Add basic constraints extension.") - fs.BoolVar(&cert.IsCA, "is-ca", cert.IsCA, "Mark certificate as CA in the basic constraints. Only takes effect if --basic-constraints is true.") - fs.Var(newMaxPathLengthValue(cert), "max-path-length", "Sets the max path length in the basic constraints.") - - fs.Var(newKeyUsageValue(&cert.KeyUsage), "key-usage", "Set the key usage. See 'pcert list' for available key usages.") - fs.Var(newExtKeyUsageValue(&cert.ExtKeyUsage), "ext-key-usage", "Set the extended key usage. See 'pcert list' for available extended key usages.") -} - -// BindKeyFlags binds flags to a pcert.KeyOptions -func BindKeyFlags(fs *pflag.FlagSet, keyOptions *pcert.KeyOptions) { - fs.Var(newKeyAlgorithmValue(&keyOptions.Algorithm), "key-alg", "Public key algorithm. See 'pcert list' for available algorithms.") - fs.IntVar(&keyOptions.Size, "key-size", keyOptions.Size, "Key Size. This defaults to 256 for ECDSA and to 2048 for RSA.") -} - -// RegisterCertificateCompletionFuncs can be used after with BindCertificateFlags to enable shell completion for certain flags -func RegisterCertificateCompletionFuncs(cmd *cobra.Command) { - _ = cmd.RegisterFlagCompletionFunc("key-usage", keyUsageCompletionFunc) - _ = cmd.RegisterFlagCompletionFunc("ext-key-usage", extKeyUsageCompletionFunc) - _ = cmd.RegisterFlagCompletionFunc("sign-alg", signAlgorithmCompletionFunc) -} - -// RegisterCertificateRequestCompletionFuncs can be used after BindCertificateRequestFlags to enable shell completion for certain flags -func RegisterCertificateRequestCompletionFuncs(cmd *cobra.Command) { - _ = cmd.RegisterFlagCompletionFunc("sign-alg", signAlgorithmCompletionFunc) -} - -// RegisterKeyCompletionFuncs can be used after with BindKeyFlags to enable shell completion for certain flags -func RegisterKeyCompletionFuncs(cmd *cobra.Command) { - _ = cmd.RegisterFlagCompletionFunc("key-alg", keyAlgorithmCompletionFunc) -} - -func BindCertificateOptionsFlags(fs *pflag.FlagSet, co *pcert.CertificateOptions) { - fs.StringSliceVar(&co.DNSNames, "dns", []string{}, "DNS subject alternative name.") - fs.StringSliceVar(&co.EmailAddresses, "email", []string{}, "Email subject alternative name.") - fs.IPSliceVar(&co.IPAddresses, "ip", []net.IP{}, "IP subject alternative name.") - fs.Var(newURISliceValue(&co.URIs), "uri", "URI subject alternative name.") - fs.Var(newSignAlgValue(&co.SignatureAlgorithm), "sign-alg", "Signature Algorithm. See 'pcert list' for available algorithms.") - - fs.Var(newTimeValue(&co.NotBefore), "not-before", fmt.Sprintf("Not valid before time in RFC3339 format (e.g. '%s').", time.Now().UTC().Format(time.RFC3339))) - fs.Var(newTimeValue(&co.NotAfter), "not-after", fmt.Sprintf("Not valid after time in RFC3339 format (e.g. '%s').", time.Now().Add(time.Hour*24*60).UTC().Format(time.RFC3339))) - fs.Var(newDurationValue(&co.Expiry), "expiry", "Validity period of the certificate. If --not-after is set this option has no effect.") - - fs.Var(newSubjectValue(&co.Subject), "subject", "Subject in the form '/C=CH/O=My Org/OU=My Team'.") - - //fs.BoolVar(&co.BasicConstraintsValid, "basic-constraints", cert.BasicConstraintsValid, "Add basic constraints extension.") - //fs.BoolVar(&co.IsCA, "is-ca", cert.IsCA, "Mark certificate as CA in the basic constraints. Only takes effect if --basic-constraints is true.") - //fs.Var(newMaxPathLengthValue(co), "max-path-length", "Sets the max path length in the basic constraints.") - - fs.Var(newKeyUsageValue(&co.KeyUsage), "key-usage", "Set the key usage. See 'pcert list' for available key usages.") - fs.Var(newExtKeyUsageValue(&co.ExtKeyUsage), "ext-key-usage", "Set the extended key usage. See 'pcert list' for available extended key usages.") -} diff --git a/cmd/pcert/request.go b/cmd/pcert/request.go index 1e4c139..52d3492 100644 --- a/cmd/pcert/request.go +++ b/cmd/pcert/request.go @@ -71,11 +71,8 @@ func newRequestCmd() *cobra.Command { }, } - BindKeyFlags(cmd.Flags(), &keyOpts) - RegisterKeyCompletionFuncs(cmd) - - BindCertificateRequestFlags(cmd.Flags(), csr) - RegisterCertificateRequestCompletionFuncs(cmd) + registerRequestFlags(cmd, csr) + registerKeyFlags(cmd, &keyOpts) return cmd } diff --git a/cmd/pcert/sign.go b/cmd/pcert/sign.go index 4bcefaf..9b0261c 100644 --- a/cmd/pcert/sign.go +++ b/cmd/pcert/sign.go @@ -120,7 +120,8 @@ func newSignCmd() *cobra.Command { cmd.Flags().StringVar(&signKeyLocation, "sign-key", signKeyLocation, "Key used to sign. If not specified but --sign-cert is specified we use the key file relative to the certificate specified with --sign-cert.") cmd.Flags().StringSliceVar(&profiles, "profile", profiles, "profile to set on the certificate (server, client, ca)") - BindCertificateOptionsFlags(cmd.Flags(), &certOpts) + + registerCertFlags(cmd, &certOpts) return cmd }