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