github.com/extrame/fabric-ca@v2.0.0-alpha+incompatible/cmd/fabric-ca-server/main_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package main 8 9 import ( 10 "crypto/x509" 11 "errors" 12 "fmt" 13 "io/ioutil" 14 "os" 15 "path" 16 "path/filepath" 17 "regexp" 18 "testing" 19 20 "github.com/cloudflare/cfssl/log" 21 "github.com/hyperledger/fabric-ca/api" 22 "github.com/hyperledger/fabric-ca/lib" 23 "github.com/hyperledger/fabric-ca/lib/metadata" 24 "github.com/hyperledger/fabric-ca/util" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 const ( 29 initYaml = "i.yaml" 30 startYaml = "s.yaml" 31 ldapTestDir = "ldapTestDir" 32 ) 33 34 var ( 35 longUserName = util.RandomString(1025) 36 ) 37 38 var ( 39 longFileName = util.RandomString(261) 40 ) 41 42 // Create a config element in unexpected format 43 var badSyntaxYaml = "bad.yaml" 44 45 // Unsupported file type 46 var unsupportedFileType = "config.txt" 47 48 type TestData struct { 49 input []string // input 50 expected string // expected result 51 } 52 53 // checkTest validates success cases 54 func checkTest(in *TestData, t *testing.T) { 55 os.Args = in.input 56 scmd := NewCommand(in.input[1], blockingStart) 57 // Execute the command 58 err := scmd.Execute() 59 if err != nil { 60 t.Errorf("FAILED:\n \tin: %v;\n \tout: %v\n \texpected: SUCCESS\n", in.input, err.Error()) 61 } else { 62 signingProfile := scmd.cfg.CAcfg.Signing.Default 63 ku, eku, unk := signingProfile.Usages() 64 // expected key usage is digital signature 65 assert.Equal(t, x509.KeyUsageDigitalSignature, ku, "Expected KeyUsageDigitalSignature") 66 assert.Equal(t, 0, len(eku), "Found %d extended usages but expected 0", len(eku)) 67 assert.Equal(t, 0, len(unk), "Found %d unknown key usages", len(unk)) 68 } 69 } 70 71 // errorTest validates error cases 72 func errorTest(in *TestData, t *testing.T) { 73 err := RunMain(in.input) 74 if err != nil { 75 matched, _ := regexp.MatchString(in.expected, err.Error()) 76 if !matched { 77 t.Errorf("FAILED:\n \tin: %v;\n \tout: %v;\n \texpected: %v\n", in.input, err.Error(), in.expected) 78 } 79 } else { 80 t.Errorf("FAILED:\n \tin: %v;\n \tout: <nil>\n \texpected: %v\n", in.input, in.expected) 81 } 82 } 83 84 func TestMain(m *testing.M) { 85 os.Setenv("FABRIC_CA_SERVER_OPERATIONS_LISTENADDRESS", "localhost:0") 86 defer os.Unsetenv("FABRIC_CA_SERVER_OPERATIONS_LISTENADDRESS") 87 88 metadata.Version = "1.1.0" 89 os.Exit(m.Run()) 90 } 91 92 func TestNoArguments(t *testing.T) { 93 err := RunMain([]string{cmdName}) 94 if err == nil { 95 assert.Error(t, errors.New("Should have resulted in an error as no agruments provided")) 96 } 97 } 98 99 func TestErrors(t *testing.T) { 100 os.Unsetenv(homeEnvVar) 101 _ = ioutil.WriteFile(badSyntaxYaml, []byte("signing: true\n"), 0644) 102 103 errorCases := []TestData{ 104 {[]string{cmdName, "init", "-c", initYaml}, "option is required"}, 105 {[]string{cmdName, "init", "-c", initYaml, "-n", "acme.com", "-b", "user::"}, "Failed to read"}, 106 {[]string{cmdName, "init", "-b", "user:pass", "-n", "acme.com", "ca.key"}, "Unrecognized arguments found"}, 107 {[]string{cmdName, "init", "-c", badSyntaxYaml, "-b", "user:pass"}, "Incorrect format"}, 108 {[]string{cmdName, "init", "-c", initYaml, "-b", fmt.Sprintf("%s:foo", longUserName)}, "than 1024 characters"}, 109 {[]string{cmdName, "init", "-c", fmt.Sprintf("/tmp/%s.yaml", longFileName), "-b", "user:pass"}, "file name too long"}, 110 {[]string{cmdName, "init", "-b", "user:pass", "-c", unsupportedFileType}, "Unsupported Config Type"}, 111 {[]string{cmdName, "init", "-c", initYaml, "-b", "user"}, "missing a colon"}, 112 {[]string{cmdName, "init", "-c", initYaml, "-b", "user:"}, "empty password"}, 113 {[]string{cmdName, "bogus", "-c", initYaml, "-b", "user:pass"}, "unknown command"}, 114 {[]string{cmdName, "start", "-c"}, "needs an argument:"}, 115 {[]string{cmdName, "start", "--csr.keyrequest.algo", "fakeAlgo"}, "Invalid algorithm: fakeAlgo"}, 116 {[]string{cmdName, "start", "--csr.keyrequest.algo", "ecdsa", "--csr.keyrequest.size", "12345"}, "Invalid ECDSA key size: 12345"}, 117 {[]string{cmdName, "start", "-c", startYaml, "-b", "user:pass", "ca.key"}, "Unrecognized arguments found"}, 118 } 119 120 for _, e := range errorCases { 121 errorTest(&e, t) 122 _ = os.Remove(initYaml) 123 } 124 } 125 126 func TestOneTimePass(t *testing.T) { 127 testDir := "oneTimePass" 128 os.RemoveAll(testDir) 129 defer os.RemoveAll(testDir) 130 // Test with "-b" option 131 err := RunMain([]string{cmdName, "init", "-b", "admin:adminpw", "--registry.maxenrollments", "1", "-H", testDir}) 132 if err != nil { 133 t.Fatalf("Failed to init server with one time passwords: %s", err) 134 } 135 } 136 137 func TestLDAP(t *testing.T) { 138 os.RemoveAll(ldapTestDir) 139 defer os.RemoveAll(ldapTestDir) 140 // Test with "-b" option 141 err := RunMain([]string{cmdName, "init", "-c", path.Join(ldapTestDir, "config.yaml"), 142 "-b", "a:b", "--ldap.enabled", "--ldap.url", "ldap://CN=admin@localhost:389/dc=example,dc=com"}) 143 if err != nil { 144 t.Errorf("Failed to init server with LDAP enabled and -b: %s", err) 145 } 146 // Try without "-b" option 147 os.RemoveAll(ldapTestDir) 148 err = RunMain([]string{cmdName, "init", "-c", path.Join(ldapTestDir, "config.yaml"), 149 "--ldap.enabled", "--ldap.url", "ldap://CN=admin@localhost:389/dc=example,dc=com"}) 150 if err != nil { 151 t.Errorf("Failed to init server with LDAP enabled and no -b: %s", err) 152 } 153 } 154 155 func TestValid(t *testing.T) { 156 os.Unsetenv(homeEnvVar) 157 blockingStart = false 158 159 os.Setenv("CA_CFG_PATH", ".") 160 validCases := []TestData{ 161 {[]string{cmdName, "init", "-b", "admin:a:d:m:i:n:p:w"}, ""}, 162 {[]string{cmdName, "init", "-d"}, ""}, 163 {[]string{cmdName, "start", "-c", startYaml, "-b", "admin:admin"}, ""}, 164 } 165 166 for _, v := range validCases { 167 checkTest(&v, t) 168 } 169 } 170 171 // Test to check that config and datasource files are created in correct location 172 // based on the arguments passed to the fabric-ca-server and environment variables 173 func TestDBLocation(t *testing.T) { 174 blockingStart = false 175 envs := []string{"FABRIC_CA_SERVER_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH", 176 "FABRIC_CA_SERVER_DB_DATASOURCE"} 177 for _, env := range envs { 178 os.Unsetenv(env) 179 } 180 181 // Invoke server with -c arg set to serverConfig/config.yml (relative path) 182 cfgFile := "serverConfig/config.yml" 183 dsFile := "serverConfig/fabric-ca-server.db" 184 args := TestData{[]string{cmdName, "start", "-b", "admin:admin", "-c", cfgFile, "-p", "7091"}, ""} 185 checkConfigAndDBLoc(t, args, cfgFile, dsFile) 186 os.RemoveAll("serverConfig") 187 188 // Invoke server with -c arg set to serverConfig1/config.yml (relative path) 189 // and FABRIC_CA_SERVER_DB_DATASOURCE env variable set to fabric-ca-srv.db (relative path) 190 os.Setenv("FABRIC_CA_SERVER_DB_DATASOURCE", "fabric-ca-srv.db") 191 cfgFile = "serverConfig1/config.yml" 192 dsFile = "serverConfig1/fabric-ca-srv.db" 193 args = TestData{[]string{cmdName, "start", "-b", "admin:admin", "-c", cfgFile, "-p", "7092"}, ""} 194 checkConfigAndDBLoc(t, args, cfgFile, dsFile) 195 os.RemoveAll("serverConfig1") 196 197 // Invoke server with -c arg set to serverConfig2/config.yml (relative path) 198 // and FABRIC_CA_SERVER_DB_DATASOURCE env variable set to /tmp/fabric-ca-srv.db (absolute path) 199 cfgFile = "serverConfig2/config.yml" 200 dsFile = os.TempDir() + "/fabric-ca-srv.db" 201 os.Setenv("FABRIC_CA_SERVER_DB_DATASOURCE", dsFile) 202 args = TestData{[]string{cmdName, "start", "-b", "admin:admin", "-c", cfgFile, "-p", "7093"}, ""} 203 checkConfigAndDBLoc(t, args, cfgFile, dsFile) 204 os.RemoveAll("serverConfig2") 205 os.Remove(dsFile) 206 207 // Invoke server with -c arg set to /tmp/config/config.yml (absolute path) 208 // and FABRIC_CA_SERVER_DB_DATASOURCE env variable set to fabric-ca-srv.db (relative path) 209 cfgDir := os.TempDir() + "/config/" 210 cfgFile = cfgDir + "config.yml" 211 dsFile = "fabric-ca-srv.db" 212 os.Setenv("FABRIC_CA_SERVER_DB_DATASOURCE", dsFile) 213 args = TestData{[]string{cmdName, "start", "-b", "admin:admin", "-c", cfgFile, "-p", "7094"}, ""} 214 checkConfigAndDBLoc(t, args, cfgFile, cfgDir+dsFile) 215 os.RemoveAll(os.TempDir() + "/config") 216 217 // Invoke server with -c arg set to /tmp/config/config.yml (absolute path) 218 // and FABRIC_CA_SERVER_DB_DATASOURCE env variable set to /tmp/fabric-ca-srv.db (absolute path) 219 cfgFile = os.TempDir() + "/config/config.yml" 220 dsFile = os.TempDir() + "/fabric-ca-srv.db" 221 os.Setenv("FABRIC_CA_SERVER_DB_DATASOURCE", dsFile) 222 args = TestData{[]string{cmdName, "start", "-b", "admin:admin", "-c", cfgFile, "-p", "7095"}, ""} 223 checkConfigAndDBLoc(t, args, cfgFile, dsFile) 224 os.RemoveAll(os.TempDir() + "/config") 225 os.Remove(dsFile) 226 os.Unsetenv("FABRIC_CA_SERVER_DB_DATASOURCE") 227 } 228 229 func TestDefaultMultiCAs(t *testing.T) { 230 blockingStart = false 231 232 err := RunMain([]string{cmdName, "start", "-p", "7055", "-c", startYaml, "-d", "-b", "user:pass", "--cacount", "4"}) 233 if err != nil { 234 t.Error("Failed to start server with multiple default CAs using the --cacount flag from command line: ", err) 235 } 236 237 if !util.FileExists("ca/ca4/fabric-ca-server_ca4.db") { 238 t.Error("Failed to create 4 default CA instances") 239 } 240 241 os.RemoveAll("ca") 242 } 243 244 func TestCACountWithAbsPath(t *testing.T) { 245 testDir := "myTestDir" 246 defer os.RemoveAll(testDir) 247 // Run init to create the ca-cert.pem 248 err := RunMain([]string{cmdName, "init", "-H", testDir, "-b", "user:pass"}) 249 if err != nil { 250 t.Fatalf("Failed to init CA: %s", err) 251 } 252 // Set the complete path to the ca-cert.pem file 253 cwd, err := os.Getwd() 254 if err != nil { 255 t.Fatalf("Failed to get current working directory: %s", err) 256 } 257 certFilePath := path.Join(cwd, testDir, "ca-cert.pem") 258 // Init again with the absolute path to ca-cert.pem and --cacount to make sure this works 259 err = RunMain([]string{cmdName, "init", "-H", testDir, "--ca.certfile", certFilePath, "--cacount", "2"}) 260 if err != nil { 261 t.Fatalf("Failed to init multi CA with absolute path: %s", err) 262 } 263 } 264 265 func TestMultiCA(t *testing.T) { 266 blockingStart = false 267 268 cleanUpMultiCAFiles() 269 defer cleanUpMultiCAFiles() 270 271 err := RunMain([]string{cmdName, "start", "-d", "-p", "7056", "-c", "../../testdata/test.yaml", "-b", "user:pass", "--cafiles", "ca/rootca/ca1/fabric-ca-server-config.yaml", "--cafiles", "ca/rootca/ca2/fabric-ca-server-config.yaml"}) 272 if err != nil { 273 t.Error("Failed to start server with multiple CAs using the --cafiles flag from command line: ", err) 274 } 275 276 if !util.FileExists("../../testdata/ca/rootca/ca2/fabric-ca2-server.db") { 277 t.Error("Failed to create 2 CA instances") 278 } 279 280 err = RunMain([]string{cmdName, "start", "-d", "-p", "7056", "-c", "../../testdata/test.yaml", "-b", "user:pass", "--cacount", "1", "--cafiles", "ca/rootca/ca1/fabric-ca-server-config.yaml", "--cafiles", "ca/rootca/ca2/fabric-ca-server-config.yaml"}) 281 if err == nil { 282 t.Error("Should have failed to start server, can't specify values for both --cacount and --cafiles") 283 } 284 } 285 286 // Tests to see that the bootstrap by default has permission to register any attibute 287 func TestRegistrarAttribute(t *testing.T) { 288 var err error 289 blockingStart = false 290 291 err = os.Setenv("FABRIC_CA_SERVER_HOME", "testregattr/server") 292 if !assert.NoError(t, err, "Failed to set environment variable") { 293 t.Fatal("Failed to set environment variable") 294 } 295 296 args := TestData{[]string{cmdName, "start", "-b", "admin:admin", "-p", "7096", "-d"}, ""} 297 os.Args = args.input 298 scmd := NewCommand(args.input[1], blockingStart) 299 // Execute the command 300 err = scmd.Execute() 301 if !assert.NoError(t, err, "Failed to start server") { 302 t.Fatal("Failed to start server") 303 } 304 305 client := getTestClient(7096, "testregattr/client") 306 307 resp, err := client.Enroll(&api.EnrollmentRequest{ 308 Name: "admin", 309 Secret: "admin", 310 }) 311 if !assert.NoError(t, err, "Failed to enroll 'admin'") { 312 t.Fatal("Failed to enroll 'admin'") 313 } 314 315 adminIdentity := resp.Identity 316 317 _, err = adminIdentity.Register(&api.RegistrationRequest{ 318 Name: "testuser", 319 Attributes: []api.Attribute{ 320 api.Attribute{ 321 Name: "hf.Revoker", 322 Value: "true", 323 }, 324 api.Attribute{ 325 Name: "hf.IntermediateCA", 326 Value: "true", 327 }, 328 api.Attribute{ 329 Name: "hf.Registrar.Roles", 330 Value: "peer,client", 331 }, 332 }, 333 }) 334 assert.NoError(t, err, "Bootstrap user 'admin' should have been able to register a user with attributes") 335 } 336 337 // TestTLSEnabledButCertfileNotSpecified tests if the server with default config starts 338 // fine with --tls.enabled and with or without --tls.certfile flag. When 339 // --tls.certfile is not specified, it should use default name 'tls-cert.pem' 340 func TestTLSEnabledButCertfileNotSpecified(t *testing.T) { 341 blockingStart = false 342 rootHomeDir := "tlsintCATestRootSrvHome" 343 err := os.RemoveAll(rootHomeDir) 344 if err != nil { 345 t.Fatalf("Failed to remove directory %s: %s", rootHomeDir, err) 346 } 347 defer os.RemoveAll(rootHomeDir) 348 349 err = RunMain([]string{cmdName, "start", "-p", "7100", "-H", rootHomeDir, "-d", "-b", "admin:admin", "--tls.enabled"}) 350 if err != nil { 351 t.Error("Server should not have failed to start when TLS is enabled and TLS cert file name is not specified...it should have used default TLS cert file name 'tls-cert.pem'", err) 352 } 353 354 // start the root server with TLS enabled 355 err = RunMain([]string{cmdName, "start", "-p", "7101", "-H", rootHomeDir, "-d", "-b", "admin:admin", "--tls.enabled", 356 "--tls.certfile", "tls-cert.pem"}) 357 if err != nil { 358 t.Error("Server should not have failed to start when TLS is enabled and TLS cert file name is specified.", err) 359 } 360 } 361 362 func TestVersion(t *testing.T) { 363 err := RunMain([]string{cmdName, "version"}) 364 if err != nil { 365 t.Error("Failed to get fabric-ca-server version: ", err) 366 } 367 } 368 369 func TestServerLogLevelCLI(t *testing.T) { 370 // Not passing in -b flag, don't need for the server to completely start to 371 // verify that the log level is correctly getting set 372 RunMain([]string{cmdName, "start", "--loglevel", "info"}) 373 assert.Equal(t, log.Level, log.LevelInfo) 374 375 RunMain([]string{cmdName, "start", "--loglevel", "debug"}) 376 assert.Equal(t, log.Level, log.LevelDebug) 377 378 RunMain([]string{cmdName, "start", "--loglevel", "warning"}) 379 assert.Equal(t, log.Level, log.LevelWarning) 380 381 RunMain([]string{cmdName, "start", "--loglevel", "fatal"}) 382 assert.Equal(t, log.Level, log.LevelFatal) 383 384 RunMain([]string{cmdName, "start", "--loglevel", "critical"}) 385 assert.Equal(t, log.Level, log.LevelCritical) 386 } 387 388 func TestServerLogLevelEnvVar(t *testing.T) { 389 // Not passing in -b flag, don't need for the server to completely start to 390 // verify that the log level is correctly getting set 391 os.Setenv("FABRIC_CA_SERVER_LOGLEVEL", "info") 392 RunMain([]string{cmdName, "start"}) 393 assert.Equal(t, log.LevelInfo, log.Level) 394 395 os.Setenv("FABRIC_CA_SERVER_LOGLEVEL", "debug") 396 RunMain([]string{cmdName, "start"}) 397 assert.Equal(t, log.LevelDebug, log.Level) 398 399 os.Setenv("FABRIC_CA_SERVER_LOGLEVEL", "warning") 400 RunMain([]string{cmdName, "start"}) 401 assert.Equal(t, log.LevelWarning, log.Level) 402 403 os.Setenv("FABRIC_CA_SERVER_LOGLEVEL", "fatal") 404 RunMain([]string{cmdName, "start"}) 405 assert.Equal(t, log.LevelFatal, log.Level) 406 407 os.Setenv("FABRIC_CA_SERVER_LOGLEVEL", "critical") 408 RunMain([]string{cmdName, "start"}) 409 assert.Equal(t, log.LevelCritical, log.Level) 410 } 411 412 // Run server with specified args and check if the configuration and datasource 413 // files exist in the specified locations 414 func checkConfigAndDBLoc(t *testing.T, args TestData, cfgFile string, dsFile string) { 415 checkTest(&args, t) 416 if _, err := os.Stat(cfgFile); os.IsNotExist(err) { 417 t.Errorf("Server configuration file is not found in the expected location: %v, TestData: %v", 418 err.Error(), args) 419 } else if _, err := os.Stat(dsFile); os.IsNotExist(err) { 420 t.Errorf("Datasource is not located in the location %s: %v, TestData: %v", 421 dsFile, err.Error(), args) 422 } 423 } 424 425 func TestClean(t *testing.T) { 426 defYaml := util.GetDefaultConfigFile(cmdName) 427 os.Remove(defYaml) 428 os.Remove(initYaml) 429 os.Remove(startYaml) 430 os.Remove(badSyntaxYaml) 431 os.Remove(fmt.Sprintf("/tmp/%s.yaml", longFileName)) 432 os.Remove(unsupportedFileType) 433 os.Remove("ca-key.pem") 434 os.Remove("ca-cert.pem") 435 os.Remove("IssuerSecretKey") 436 os.Remove("IssuerPublicKey") 437 os.Remove("IssuerRevocationPublicKey") 438 os.Remove("fabric-ca-server.db") 439 os.RemoveAll("keystore") 440 os.RemoveAll("msp") 441 os.RemoveAll("../../testdata/msp") 442 os.Remove("../../testdata/fabric-ca-server.db") 443 os.Remove("../../testdata/ca-cert.pem") 444 os.Remove("../../testdata/IssuerSecretKey") 445 os.Remove("../../testdata/IssuerPublicKey") 446 os.Remove("../../testdata/IssuerRevocationPublicKey") 447 os.RemoveAll(ldapTestDir) 448 os.RemoveAll("testregattr") 449 } 450 451 func cleanUpMultiCAFiles() { 452 caFolder := "../../testdata/ca/rootca" 453 nestedFolders := []string{"ca1", "ca2"} 454 removeFiles := []string{"msp", "ca-cert.pem", "ca-key.pem", "fabric-ca-server.db", 455 "fabric-ca2-server.db", "IssuerSecretKey", "IssuerPublicKey", "IssuerRevocationPublicKey"} 456 457 for _, nestedFolder := range nestedFolders { 458 path := filepath.Join(caFolder, nestedFolder) 459 for _, file := range removeFiles { 460 os.RemoveAll(filepath.Join(path, file)) 461 } 462 os.RemoveAll(filepath.Join(path, "msp")) 463 } 464 465 os.Remove("../../testdata/test.yaml") 466 } 467 468 func getTestClient(port int, homeDir string) *lib.Client { 469 return &lib.Client{ 470 Config: &lib.ClientConfig{URL: fmt.Sprintf("http://localhost:%d", port)}, 471 HomeDir: homeDir, 472 } 473 }