Commit 84293023 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: stop depending on forked pflag functionality for inserting headers in command help messages

parent 3f743367
package cmd
import (
"regexp"
"strings"
)
// headerFlagsTemplate is copied from cobra.Command.UsageTemplate, modified to include an invocation
// of insertHeaders on the flags, which intersperses the flags with headers.
var headerFlagsTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces | insertHeaders .CommandPath}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
var flagHeaders = map[string]map[string]string{}
func insertHeaders(cmdPath string, flags string) string {
headers := flagHeaders[cmdPath]
if len(headers) == 0 {
return flags
}
in := strings.Split(flags, "\n")
out := make([]string, 0, len(in)+len(headers))
r := regexp.MustCompile(`^\s+(-\w, )?--([^ ]*)`)
for _, line := range in {
matches := r.FindStringSubmatch(line)
header := headers[matches[2]]
if header != "" {
out = append(out, "\n"+header)
}
out = append(out, line)
}
return strings.Join(out, "\n")
}
...@@ -31,6 +31,16 @@ var myirmaServerCmd = &cobra.Command{ ...@@ -31,6 +31,16 @@ var myirmaServerCmd = &cobra.Command{
func init() { func init() {
keyshareRootCmd.AddCommand(myirmaServerCmd) keyshareRootCmd.AddCommand(myirmaServerCmd)
myirmaServerCmd.SetUsageTemplate(headerFlagsTemplate)
flagHeaders["irma keyshare myirmaserver"] = map[string]string{
"port": "Server address and port to listen on",
"db-type": "Database configuration",
"keyshare-attributes": "IRMA session configuration",
"email-server": "Email configuration (leave empty to disable sending emails)",
"tls-cert": "TLS configuration (leave empty to disable TLS)",
"verbose": "Other options",
}
flags := myirmaServerCmd.Flags() flags := myirmaServerCmd.Flags()
flags.SortFlags = false flags.SortFlags = false
...@@ -46,16 +56,13 @@ func init() { ...@@ -46,16 +56,13 @@ func init() {
flags.IntP("port", "p", 8080, "port at which to listen") flags.IntP("port", "p", 8080, "port at which to listen")
flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)") flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)")
flags.StringSlice("cors-allowed-origins", nil, "CORS allowed origins") flags.StringSlice("cors-allowed-origins", nil, "CORS allowed origins")
flags.Lookup("port").Header = `Server address and port to listen on`
flags.String("db-type", string(myirmaserver.DBTypePostgres), "Type of database to connect keyshare server to") flags.String("db-type", string(myirmaserver.DBTypePostgres), "Type of database to connect keyshare server to")
flags.String("db", "", "Database server connection string") flags.String("db", "", "Database server connection string")
flags.Lookup("db-type").Header = `Database configuration`
flags.StringSlice("keyshare-attributes", nil, "Attributes allowed for login to myirma") flags.StringSlice("keyshare-attributes", nil, "Attributes allowed for login to myirma")
flags.StringSlice("email-attributes", nil, "Attributes allowed for adding email addresses") flags.StringSlice("email-attributes", nil, "Attributes allowed for adding email addresses")
flags.Int("session-lifetime", myirmaserver.SessionLifetimeDefault, "Session lifetime in seconds") flags.Int("session-lifetime", myirmaserver.SessionLifetimeDefault, "Session lifetime in seconds")
flags.Lookup("keyshare-attributes").Header = `IRMA session configuration`
flags.String("email-server", "", "Email server to use for sending email address confirmation emails") flags.String("email-server", "", "Email server to use for sending email address confirmation emails")
flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)") flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)")
...@@ -71,20 +78,17 @@ func init() { ...@@ -71,20 +78,17 @@ func init() {
flags.StringToString("delete-account-subjects", nil, "Translated subject lines for the delete account email") flags.StringToString("delete-account-subjects", nil, "Translated subject lines for the delete account email")
flags.StringToString("delete-account-files", nil, "Translated emails for the delete account email") flags.StringToString("delete-account-files", nil, "Translated emails for the delete account email")
flags.Int("delete-delay", 0, "delay in days before a user or email address deletion becomes effective") flags.Int("delete-delay", 0, "delay in days before a user or email address deletion becomes effective")
flags.Lookup("email-server").Header = `Email configuration (leave empty to disable sending emails)`
flags.String("tls-cert", "", "TLS certificate (chain)") flags.String("tls-cert", "", "TLS certificate (chain)")
flags.String("tls-cert-file", "", "path to TLS certificate (chain)") flags.String("tls-cert-file", "", "path to TLS certificate (chain)")
flags.String("tls-privkey", "", "TLS private key") flags.String("tls-privkey", "", "TLS private key")
flags.String("tls-privkey-file", "", "path to TLS private key") flags.String("tls-privkey-file", "", "path to TLS private key")
flags.Bool("no-tls", false, "Disable TLS") flags.Bool("no-tls", false, "Disable TLS")
flags.Lookup("tls-cert").Header = `TLS configuration (leave empty to disable TLS)`
flags.CountP("verbose", "v", "verbose (repeatable)") flags.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet") flags.BoolP("quiet", "q", false, "quiet")
flags.Bool("log-json", false, "Log in JSON format") flags.Bool("log-json", false, "Log in JSON format")
flags.Bool("production", false, "Production mode") flags.Bool("production", false, "Production mode")
flags.Lookup("verbose").Header = `Other options`
} }
func configureMyirmaServer(cmd *cobra.Command) (*myirmaserver.Configuration, error) { func configureMyirmaServer(cmd *cobra.Command) (*myirmaserver.Configuration, error) {
......
...@@ -32,6 +32,17 @@ var keyshareServerCmd = &cobra.Command{ ...@@ -32,6 +32,17 @@ var keyshareServerCmd = &cobra.Command{
func init() { func init() {
keyshareRootCmd.AddCommand(keyshareServerCmd) keyshareRootCmd.AddCommand(keyshareServerCmd)
keyshareServerCmd.SetUsageTemplate(headerFlagsTemplate)
flagHeaders["irma keyshare server"] = map[string]string{
"port": "Server address and port to listen on",
"db-type": "Database configuration",
"jwt-privkey": "Cryptographic keys",
"keyshare-attribute": "Keyshare server attribute issued during registration",
"email-server": "Email configuration (leave empty to disable sending emails)",
"tls-cert": "TLS configuration (leave empty to disable TLS)",
"verbose": "Other options",
}
flags := keyshareServerCmd.Flags() flags := keyshareServerCmd.Flags()
flags.SortFlags = false flags.SortFlags = false
flags.StringP("config", "c", "", "path to configuration file") flags.StringP("config", "c", "", "path to configuration file")
...@@ -43,11 +54,9 @@ func init() { ...@@ -43,11 +54,9 @@ func init() {
flags.IntP("port", "p", 8080, "port at which to listen") flags.IntP("port", "p", 8080, "port at which to listen")
flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)") flags.StringP("listen-addr", "l", "", "address at which to listen (default 0.0.0.0)")
flags.Lookup("port").Header = `Server address and port to listen on`
flags.String("db-type", string(keyshareserver.DBTypePostgres), "Type of database to connect keyshare server to") flags.String("db-type", string(keyshareserver.DBTypePostgres), "Type of database to connect keyshare server to")
flags.String("db", "", "Database server connection string") flags.String("db", "", "Database server connection string")
flags.Lookup("db-type").Header = `Database configuration`
flags.String("jwt-privkey", "", "Private jwt key of keyshare server") flags.String("jwt-privkey", "", "Private jwt key of keyshare server")
flags.String("jwt-privkey-file", "", "Path to file containing private jwt key of keyshare server") flags.String("jwt-privkey-file", "", "Path to file containing private jwt key of keyshare server")
...@@ -56,10 +65,8 @@ func init() { ...@@ -56,10 +65,8 @@ func init() {
flags.Int("jwt-pin-expiry", keysharecore.JWTPinExpiryDefault, "Expiry of PIN JWT in seconds") flags.Int("jwt-pin-expiry", keysharecore.JWTPinExpiryDefault, "Expiry of PIN JWT in seconds")
flags.String("storage-primary-keyfile", "", "Primary key used for encrypting and decrypting secure containers") flags.String("storage-primary-keyfile", "", "Primary key used for encrypting and decrypting secure containers")
flags.StringSlice("storage-fallback-keyfile", nil, "Fallback key(s) used to decrypt older secure containers") flags.StringSlice("storage-fallback-keyfile", nil, "Fallback key(s) used to decrypt older secure containers")
flags.Lookup("jwt-privkey").Header = `Cryptographic keys`
flags.String("keyshare-attribute", "", "Attribute identifier that contains username") flags.String("keyshare-attribute", "", "Attribute identifier that contains username")
flags.Lookup("keyshare-attribute").Header = `Keyshare server attribute issued during registration`
flags.String("email-server", "", "Email server to use for sending email address confirmation emails") flags.String("email-server", "", "Email server to use for sending email address confirmation emails")
flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)") flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)")
...@@ -70,20 +77,17 @@ func init() { ...@@ -70,20 +77,17 @@ func init() {
flags.StringToString("registration-email-subjects", nil, "Translated subject lines for the registration email") flags.StringToString("registration-email-subjects", nil, "Translated subject lines for the registration email")
flags.StringToString("registration-email-files", nil, "Translated emails for the registration email") flags.StringToString("registration-email-files", nil, "Translated emails for the registration email")
flags.StringToString("verification-url", nil, "Base URL for the email verification link (localized)") flags.StringToString("verification-url", nil, "Base URL for the email verification link (localized)")
flags.Lookup("email-server").Header = `Email configuration (leave empty to disable sending emails)`
flags.String("tls-cert", "", "TLS certificate (chain)") flags.String("tls-cert", "", "TLS certificate (chain)")
flags.String("tls-cert-file", "", "path to TLS certificate (chain)") flags.String("tls-cert-file", "", "path to TLS certificate (chain)")
flags.String("tls-privkey", "", "TLS private key") flags.String("tls-privkey", "", "TLS private key")
flags.String("tls-privkey-file", "", "path to TLS private key") flags.String("tls-privkey-file", "", "path to TLS private key")
flags.Bool("no-tls", false, "Disable TLS") flags.Bool("no-tls", false, "Disable TLS")
flags.Lookup("tls-cert").Header = `TLS configuration (leave empty to disable TLS)`
flags.CountP("verbose", "v", "verbose (repeatable)") flags.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet") flags.BoolP("quiet", "q", false, "quiet")
flags.Bool("log-json", false, "Log in JSON format") flags.Bool("log-json", false, "Log in JSON format")
flags.Bool("production", false, "Production mode") flags.Bool("production", false, "Production mode")
flags.Lookup("verbose").Header = `Other options`
} }
func configureKeyshareServer(cmd *cobra.Command) (*keyshareserver.Configuration, error) { func configureKeyshareServer(cmd *cobra.Command) (*keyshareserver.Configuration, error) {
......
...@@ -20,17 +20,23 @@ var keyshareTaskCmd = &cobra.Command{ ...@@ -20,17 +20,23 @@ var keyshareTaskCmd = &cobra.Command{
func init() { func init() {
keyshareRootCmd.AddCommand(keyshareTaskCmd) keyshareRootCmd.AddCommand(keyshareTaskCmd)
keyshareTaskCmd.SetUsageTemplate(headerFlagsTemplate)
flagHeaders["irma keyshare tasks"] = map[string]string{
"db": "Database configuration",
"expiry-delay": "Time period configuraiton",
"email-server": "Email configuration (leave empty to disable sending emails)",
"verbose": "Other options",
}
flags := keyshareTaskCmd.Flags() flags := keyshareTaskCmd.Flags()
flags.SortFlags = false flags.SortFlags = false
flags.StringP("config", "c", "", "path to configuration file") flags.StringP("config", "c", "", "path to configuration file")
flags.String("db", "", "Database server connection string") flags.String("db", "", "Database server connection string")
flags.Lookup("db").Header = `Database configuration`
flags.Int("expiry-delay", 365, "Number of days of inactivity until account expires") flags.Int("expiry-delay", 365, "Number of days of inactivity until account expires")
flags.Int("delete-delay", 30, "Number of days until expired account should be deleted") flags.Int("delete-delay", 30, "Number of days until expired account should be deleted")
flags.Lookup("expiry-delay").Header = `Time period configuraiton`
flags.String("email-server", "", "Email server to use for sending email address confirmation emails") flags.String("email-server", "", "Email server to use for sending email address confirmation emails")
flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)") flags.String("email-hostname", "", "Hostname used in email server tls certificate (leave empty when mail server does not use tls)")
...@@ -40,12 +46,10 @@ func init() { ...@@ -40,12 +46,10 @@ func init() {
flags.String("default-language", "en", "Default language, used as fallback when users preferred language is not available") flags.String("default-language", "en", "Default language, used as fallback when users preferred language is not available")
flags.StringToString("expired-email-subjects", nil, "Translated subject lines for the expired account email") flags.StringToString("expired-email-subjects", nil, "Translated subject lines for the expired account email")
flags.StringToString("expired-email-files", nil, "Translated emails for the expired account email") flags.StringToString("expired-email-files", nil, "Translated emails for the expired account email")
flags.Lookup("email-server").Header = `Email configuration (leave empty to disable sending emails)`
flags.CountP("verbose", "v", "verbose (repeatable)") flags.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet") flags.BoolP("quiet", "q", false, "quiet")
flags.Bool("log-json", false, "Log in JSON format") flags.Bool("log-json", false, "Log in JSON format")
flags.Lookup("verbose").Header = `Other options`
} }
func configureKeyshareTasks(cmd *cobra.Command) *tasks.Configuration { func configureKeyshareTasks(cmd *cobra.Command) *tasks.Configuration {
......
...@@ -37,6 +37,8 @@ func Execute() { ...@@ -37,6 +37,8 @@ func Execute() {
func init() { func init() {
RootCmd.AddCommand(versionCmd) RootCmd.AddCommand(versionCmd)
cobra.AddTemplateFunc("insertHeaders", insertHeaders)
} }
func die(message string, err error) { func die(message string, err error) {
......
...@@ -67,8 +67,15 @@ func init() { ...@@ -67,8 +67,15 @@ func init() {
} }
func setFlags(cmd *cobra.Command, production bool) error { func setFlags(cmd *cobra.Command, production bool) error {
flags := cmd.Flags() cmd.SetUsageTemplate(headerFlagsTemplate)
flags.SortFlags = false flagHeaders["irma server"] = map[string]string{
"port": "Server address and port to listen on",
"no-auth": "Requestor authentication and default requestor permissions",
"jwt-issuer": "JWT configuration",
"tls-cert": "TLS configuration (leave empty to disable TLS)",
"email": "Email address (see README for more info)",
"verbose": "Other options",
}
var defaulturl string var defaulturl string
if !production { if !production {
...@@ -79,6 +86,9 @@ func setFlags(cmd *cobra.Command, production bool) error { ...@@ -79,6 +86,9 @@ func setFlags(cmd *cobra.Command, production bool) error {
schemespath := irma.DefaultSchemesPath() schemespath := irma.DefaultSchemesPath()
flags := cmd.Flags()
flags.SortFlags = false
flags.StringP("config", "c", "", "path to configuration file") flags.StringP("config", "c", "", "path to configuration file")
flags.StringP("schemes-path", "s", schemespath, "path to irma_configuration") flags.StringP("schemes-path", "s", schemespath, "path to irma_configuration")
flags.String("schemes-assets-path", "", "if specified, copy schemes from here into --schemes-path") flags.String("schemes-assets-path", "", "if specified, copy schemes from here into --schemes-path")
...@@ -96,7 +106,6 @@ func setFlags(cmd *cobra.Command, production bool) error { ...@@ -96,7 +106,6 @@ func setFlags(cmd *cobra.Command, production bool) error {
flags.StringP("api-prefix", "a", "/", "prefix API endpoints with this string, e.g. POST /session becomes POST {api-prefix}/session") flags.StringP("api-prefix", "a", "/", "prefix API endpoints with this string, e.g. POST /session becomes POST {api-prefix}/session")
flags.Int("client-port", 0, "if specified, start a separate server for the IRMA app at this port") flags.Int("client-port", 0, "if specified, start a separate server for the IRMA app at this port")
flags.String("client-listen-addr", "", "address at which server for IRMA app listens") flags.String("client-listen-addr", "", "address at which server for IRMA app listens")
flags.Lookup("port").Header = `Server address and port to listen on`
flags.Bool("no-auth", !production, "whether or not to authenticate requestors (and reject all authenticated requests)") flags.Bool("no-auth", !production, "whether or not to authenticate requestors (and reject all authenticated requests)")
flags.String("requestors", "", "requestor configuration (in JSON)") flags.String("requestors", "", "requestor configuration (in JSON)")
...@@ -110,7 +119,6 @@ func setFlags(cmd *cobra.Command, production bool) error { ...@@ -110,7 +119,6 @@ func setFlags(cmd *cobra.Command, production bool) error {
flags.StringSlice("revoke-perms", nil, "list of credentials that all requestors may revoke") flags.StringSlice("revoke-perms", nil, "list of credentials that all requestors may revoke")
flags.Bool("skip-private-keys-check", false, "whether or not to skip checking whether the private keys that requestors have permission for using are present in the configuration") flags.Bool("skip-private-keys-check", false, "whether or not to skip checking whether the private keys that requestors have permission for using are present in the configuration")
flags.String("static-sessions", "", "preconfigured static sessions (in JSON)") flags.String("static-sessions", "", "preconfigured static sessions (in JSON)")
flags.Lookup("no-auth").Header = `Requestor authentication and default requestor permissions`
flags.String("revocation-settings", "", "revocation settings (in JSON)") flags.String("revocation-settings", "", "revocation settings (in JSON)")
...@@ -120,7 +128,6 @@ func setFlags(cmd *cobra.Command, production bool) error { ...@@ -120,7 +128,6 @@ func setFlags(cmd *cobra.Command, production bool) error {
flags.Int("max-request-age", 300, "max age in seconds of a session request JWT") flags.Int("max-request-age", 300, "max age in seconds of a session request JWT")
flags.Bool("allow-unsigned-callbacks", false, "Allow callbackUrl in session requests when no JWT privatekey is installed (potentially unsafe)") flags.Bool("allow-unsigned-callbacks", false, "Allow callbackUrl in session requests when no JWT privatekey is installed (potentially unsafe)")
flags.Bool("augment-client-return-url", false, "Augment the client return url with the server session token if present") flags.Bool("augment-client-return-url", false, "Augment the client return url with the server session token if present")
flags.Lookup("jwt-issuer").Header = `JWT configuration`
flags.String("tls-cert", "", "TLS certificate (chain)") flags.String("tls-cert", "", "TLS certificate (chain)")
flags.String("tls-cert-file", "", "path to TLS certificate (chain)") flags.String("tls-cert-file", "", "path to TLS certificate (chain)")
...@@ -131,17 +138,14 @@ func setFlags(cmd *cobra.Command, production bool) error { ...@@ -131,17 +138,14 @@ func setFlags(cmd *cobra.Command, production bool) error {
flags.String("client-tls-privkey", "", "TLS private key for IRMA app server") flags.String("client-tls-privkey", "", "TLS private key for IRMA app server")
flags.String("client-tls-privkey-file", "", "path to TLS private key for IRMA app server") flags.String("client-tls-privkey-file", "", "path to TLS private key for IRMA app server")
flags.Bool("no-tls", false, "Disable TLS") flags.Bool("no-tls", false, "Disable TLS")
flags.Lookup("tls-cert").Header = "TLS configuration (leave empty to disable TLS)"
flags.StringP("email", "e", "", "Email address of server admin, for incidental notifications such as breaking API changes") flags.StringP("email", "e", "", "Email address of server admin, for incidental notifications such as breaking API changes")
flags.Bool("no-email", !production, "Opt out of providing an email address with --email") flags.Bool("no-email", !production, "Opt out of providing an email address with --email")
flags.Lookup("email").Header = "Email address (see README for more info)"
flags.CountP("verbose", "v", "verbose (repeatable)") flags.CountP("verbose", "v", "verbose (repeatable)")
flags.BoolP("quiet", "q", false, "quiet") flags.BoolP("quiet", "q", false, "quiet")
flags.Bool("log-json", false, "Log in JSON format") flags.Bool("log-json", false, "Log in JSON format")
flags.Bool("production", false, "Production mode") flags.Bool("production", false, "Production mode")
flags.Lookup("verbose").Header = `Other options`
return nil return nil
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment