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 }