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