github.com/extrame/fabric-ca@v2.0.0-alpha+incompatible/cmd/fabric-ca-client/command/clientcmd.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package command
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	"github.com/cloudflare/cfssl/log"
    17  	"github.com/hyperledger/fabric-ca/lib"
    18  	"github.com/hyperledger/fabric-ca/lib/metadata"
    19  	"github.com/hyperledger/fabric-ca/util"
    20  	"github.com/pkg/profile"
    21  	"github.com/spf13/cobra"
    22  	"github.com/spf13/viper"
    23  )
    24  
    25  const (
    26  	fabricCAClientProfileMode = "FABRIC_CA_CLIENT_PROFILE_MODE"
    27  	extraArgsError            = "Unrecognized arguments found: %v\n\n%s"
    28  )
    29  
    30  const (
    31  	client    = "client"
    32  	enroll    = "enroll"
    33  	reenroll  = "reenroll"
    34  	register  = "register"
    35  	revoke    = "revoke"
    36  	getcacert = "getcacert"
    37  	getcainfo = "getcainfo"
    38  	gencsr    = "gencsr"
    39  )
    40  
    41  // Command interface initializes client command and loads an identity
    42  type Command interface {
    43  	// Initializes the client command configuration
    44  	ConfigInit() error
    45  	// Returns the name of the configuration file
    46  	GetCfgFileName() string
    47  	// Loads the credentials of an identity that are in the msp directory specified to this command
    48  	LoadMyIdentity() (*lib.Identity, error)
    49  	// Returns lib.ClientCfg instance associated with this comamnd
    50  	GetClientCfg() *lib.ClientConfig
    51  	// Returns viper instance associated with this comamnd
    52  	GetViper() *viper.Viper
    53  	// Returns the client's home directoty
    54  	GetHomeDirectory() string
    55  	// Set the default level to be something other than 'info'
    56  	SetDefaultLogLevel(string)
    57  }
    58  
    59  type crlArgs struct {
    60  	// Genenerate CRL with all the certificates that were revoked after this timestamp
    61  	RevokedAfter string `help:"Generate CRL with certificates that were revoked after this UTC timestamp (in RFC3339 format)"`
    62  	// Genenerate CRL with all the certificates that were revoked before this timestamp
    63  	RevokedBefore string `help:"Generate CRL with certificates that were revoked before this UTC timestamp (in RFC3339 format)"`
    64  	// Genenerate CRL with all the certificates that expire after this timestamp
    65  	ExpireAfter string `help:"Generate CRL with certificates that expire after this UTC timestamp (in RFC3339 format)"`
    66  	// Genenerate CRL with all the certificates that expire before this timestamp
    67  	ExpireBefore string `help:"Generate CRL with certificates that expire before this UTC timestamp (in RFC3339 format)"`
    68  }
    69  
    70  type revokeArgs struct {
    71  	// GenCRL specifies whether to generate a CRL
    72  	GenCRL bool `def:"false" json:"gencrl,omitempty" opt:"" help:"Generates a CRL that contains all revoked certificates"`
    73  }
    74  
    75  // ClientCmd encapsulates cobra command that provides command line interface
    76  // for the Fabric CA client and the configuration used by the Fabric CA client
    77  type ClientCmd struct {
    78  	// name of the sub command
    79  	name string
    80  	// rootCmd is the base command for the Hyerledger Fabric CA client
    81  	rootCmd *cobra.Command
    82  	// My viper instance
    83  	myViper *viper.Viper
    84  	// cfgFileName is the name of the configuration file
    85  	cfgFileName string
    86  	// homeDirectory is the location of the client's home directory
    87  	homeDirectory string
    88  	// clientCfg is the client's configuration
    89  	clientCfg *lib.ClientConfig
    90  	// cfgAttrs are the attributes specified via flags or env variables
    91  	// and translated to Attributes field in registration
    92  	cfgAttrs []string
    93  	// cfgAttrReqs are the attribute requests specified via flags or env variables
    94  	// and translated to the AttrReqs field in enrollment
    95  	cfgAttrReqs []string
    96  	// cfgCsrNames are the certificate signing request names specified via flags
    97  	// or env variables
    98  	cfgCsrNames []string
    99  	// csrCommonName is the certificate signing request common name specified via the flag
   100  	csrCommonName string
   101  	// gencrl command argument values
   102  	crlParams crlArgs
   103  	// revoke command argument values
   104  	revokeParams revokeArgs
   105  	// profileMode is the profiling mode, cpu or mem or empty
   106  	profileMode string
   107  	// profileInst is the profiling instance object
   108  	profileInst interface {
   109  		Stop()
   110  	}
   111  	// Dynamically configuring identities
   112  	dynamicIdentity identityArgs
   113  	// Dynamically configuring affiliations
   114  	dynamicAffiliation affiliationArgs
   115  	// Set to log level
   116  	logLevel string
   117  }
   118  
   119  // NewCommand returns new ClientCmd ready for running
   120  func NewCommand(name string) *ClientCmd {
   121  	c := &ClientCmd{
   122  		myViper: viper.New(),
   123  	}
   124  	c.name = strings.ToLower(name)
   125  	c.init()
   126  	return c
   127  }
   128  
   129  // Execute runs this ClientCmd
   130  func (c *ClientCmd) Execute() error {
   131  	return c.rootCmd.Execute()
   132  }
   133  
   134  // init initializes the ClientCmd instance
   135  // It intializes the cobra root and sub commands and
   136  // registers command flgs with viper
   137  func (c *ClientCmd) init() {
   138  	c.rootCmd = &cobra.Command{
   139  		Use:   cmdName,
   140  		Short: longName,
   141  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   142  			err := c.checkAndEnableProfiling()
   143  			if err != nil {
   144  				return err
   145  			}
   146  			util.CmdRunBegin(c.myViper)
   147  			cmd.SilenceUsage = true
   148  			return nil
   149  		},
   150  		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
   151  			if c.profileMode != "" && c.profileInst != nil {
   152  				c.profileInst.Stop()
   153  			}
   154  			return nil
   155  		},
   156  	}
   157  	c.rootCmd.AddCommand(c.newRegisterCommand(),
   158  		newEnrollCmd(c).getCommand(),
   159  		c.newReenrollCommand(),
   160  		c.newRevokeCommand(),
   161  		newGetCAInfoCmd(c).getCommand(),
   162  		c.newGenCsrCommand(),
   163  		c.newGenCRLCommand(),
   164  		c.newIdentityCommand(),
   165  		c.newAffiliationCommand(),
   166  		createCertificateCommand(c))
   167  	c.rootCmd.AddCommand(&cobra.Command{
   168  		Use:   "version",
   169  		Short: "Prints Fabric CA Client version",
   170  		Run: func(cmd *cobra.Command, args []string) {
   171  			fmt.Print(metadata.GetVersionInfo(cmdName))
   172  		},
   173  	})
   174  	c.registerFlags()
   175  	log.Level = log.LevelInfo
   176  }
   177  
   178  // registerFlags registers command flags with viper
   179  func (c *ClientCmd) registerFlags() {
   180  	// Get the default config file path
   181  	cfg := util.GetDefaultConfigFile(cmdName)
   182  
   183  	// All env variables must be prefixed
   184  	c.myViper.SetEnvPrefix(envVarPrefix)
   185  	c.myViper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   186  
   187  	host, err := os.Hostname()
   188  	if err != nil {
   189  		log.Error(err)
   190  	}
   191  
   192  	// Set global flags used by all commands
   193  	pflags := c.rootCmd.PersistentFlags()
   194  	pflags.StringVarP(&c.cfgFileName, "config", "c", "", "Configuration file")
   195  	pflags.MarkHidden("config")
   196  	// Don't want to use the default parameter for StringVarP. Need to be able to identify if home directory was explicitly set
   197  	pflags.StringVarP(&c.homeDirectory, "home", "H", "", fmt.Sprintf("Client's home directory (default \"%s\")", filepath.Dir(cfg)))
   198  	pflags.StringSliceVarP(
   199  		&c.cfgAttrs, "id.attrs", "", nil, "A list of comma-separated attributes of the form <name>=<value> (e.g. foo=foo1,bar=bar1)")
   200  	pflags.StringSliceVarP(
   201  		&c.cfgAttrReqs, "enrollment.attrs", "", nil, "A list of comma-separated attribute requests of the form <name>[:opt] (e.g. foo,bar:opt)")
   202  	util.FlagString(c.myViper, pflags, "myhost", "m", host,
   203  		"Hostname to include in the certificate signing request during enrollment")
   204  	pflags.StringSliceVarP(
   205  		&c.cfgCsrNames, "csr.names", "", nil, "A list of comma-separated CSR names of the form <name>=<value> (e.g. C=CA,O=Org1)")
   206  
   207  	c.clientCfg = &lib.ClientConfig{}
   208  	tags := map[string]string{
   209  		"help.csr.cn":                "The common name field of the certificate signing request",
   210  		"help.csr.serialnumber":      "The serial number in a certificate signing request",
   211  		"help.csr.hosts":             "A list of comma-separated host names in a certificate signing request",
   212  		"skip.csp.pluginopts.config": "true", // Skipping because this a map
   213  	}
   214  	err = util.RegisterFlags(c.myViper, pflags, c.clientCfg, tags)
   215  	if err != nil {
   216  		panic(err)
   217  	}
   218  }
   219  
   220  // checkAndEnableProfiling checks for the FABRIC_CA_CLIENT_PROFILE_MODE
   221  // env variable, if it is set to "cpu", cpu profiling is enbled;
   222  // if it is set to "heap", heap profiling is enabled
   223  func (c *ClientCmd) checkAndEnableProfiling() error {
   224  	c.profileMode = strings.ToLower(os.Getenv(fabricCAClientProfileMode))
   225  	if c.profileMode != "" {
   226  		wd, err := os.Getwd()
   227  		if err != nil {
   228  			wd = os.Getenv("HOME")
   229  		}
   230  		opt := profile.ProfilePath(wd)
   231  		switch c.profileMode {
   232  		case "cpu":
   233  			c.profileInst = profile.Start(opt, profile.CPUProfile)
   234  		case "heap":
   235  			c.profileInst = profile.Start(opt, profile.MemProfileRate(2048))
   236  		default:
   237  			msg := fmt.Sprintf("Invalid value for the %s environment variable; found '%s', expecting 'cpu' or 'heap'",
   238  				fabricCAClientProfileMode, c.profileMode)
   239  			return errors.New(msg)
   240  		}
   241  	}
   242  	return nil
   243  }
   244  
   245  // Certain client commands can only be executed if enrollment credentials
   246  // are present
   247  func (c *ClientCmd) requiresEnrollment() bool {
   248  	return c.name != enroll && c.name != getcacert && c.name != getcainfo && c.name != gencsr
   249  }
   250  
   251  // Create default client configuration file only during an enroll or gencsr command
   252  func (c *ClientCmd) shouldCreateDefaultConfig() bool {
   253  	return c.name == enroll || c.name == gencsr
   254  }
   255  
   256  func (c *ClientCmd) requiresUser() bool {
   257  	return c.name != gencsr
   258  }
   259  
   260  // LoadMyIdentity loads the client's identity
   261  func (c *ClientCmd) LoadMyIdentity() (*lib.Identity, error) {
   262  	client := &lib.Client{
   263  		HomeDir: filepath.Dir(c.cfgFileName),
   264  		Config:  c.clientCfg,
   265  	}
   266  
   267  	id, err := client.LoadMyIdentity()
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	return id, nil
   273  }
   274  
   275  // GetClientCfg returns client configuration
   276  func (c *ClientCmd) GetClientCfg() *lib.ClientConfig {
   277  	return c.clientCfg
   278  }
   279  
   280  // GetCfgFileName returns name of the client command configuration file
   281  func (c *ClientCmd) GetCfgFileName() string {
   282  	return c.cfgFileName
   283  }
   284  
   285  // GetViper returns the viper instance
   286  func (c *ClientCmd) GetViper() *viper.Viper {
   287  	return c.myViper
   288  }
   289  
   290  // SetDefaultLogLevel sets the default log level for a command to a specific level
   291  func (c *ClientCmd) SetDefaultLogLevel(logLevel string) {
   292  	c.logLevel = logLevel
   293  }