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