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  }