diff --git a/README.md b/README.md index 24dc2579f..d7739050f 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ Run the following command to start fabric-ca server: # fabric-ca server start -config ../testdata/server-config.json ``` -It is now listening on localhost port 8888. +It is now listening on localhost port 7054. You can customize your fabric-ca config file at `../testdata/server-config.json`. For example, if you want to disable authentication, you can do so by setting `authentication` to @@ -241,13 +241,30 @@ Auhentication is added by fabric-ca since CFSSL does not perform authentication. basic authentication header is required for the enroll request. All other requests to the fabric-ca server will require a JWT-like token, but this work is not yet complete. +### Create Client Configuration File + +The client requires a configuration file to enable TLS and successfully connect +to server. A sample client configuration file can be found at +`../testdata/client-config.json`. The configuration file is passed in using +a config flag. + + +The table below defines all the properties that can be set in the config file. + +| Property | Description | +|-------------|--------------------------------------------------------------| +|serverURL | URL of the server | +|ca_certfiles | File path to root certificate of which server certificate is signed by | +|keyfile | File path to client TLS key on file system | +|certfile | File path to client TLS certificate on file system | + ### Enroll the admin client See the `FABRIC_CA/testdata/server-config.json` file and note the "admin" user with a password of "adminpw". The following command gets an ecert for the admin user. ``` -# fabric-ca client enroll admin adminpw http://localhost:8888 +# fabric-ca client enroll -config ../testdata/client-config.json admin adminpw http://localhost:7054 ``` The enrollment certificate is stored at `$FABRIC_CA_ENROLLMENT_DIR/cert.pem` by default, but a different @@ -268,7 +285,7 @@ command except no username or password is required. Instead, your previously st key is used to authenticate to the fabric-ca server. ``` -# fabric-ca client reenroll http://localhost:8888 +# fabric-ca client reenroll -config ../testdata/client-config.json http://localhost:7054 ``` The enrollment certificate and enrollment key are stored in the same location as described in the previous section for the `enroll` command. @@ -276,7 +293,7 @@ The enrollment certificate and enrollment key are stored in the same location as You can specify a new Certificate Signing Request JSON information when issue the reenroll command ``` -# fabric-ca client reenroll http://localhost:8888 ../testdata/csr.json +# fabric-ca client reenroll -config ../testdata/client-config.json http://localhost:7054 ../testdata/csr.json ``` ### Register a new user @@ -311,7 +328,7 @@ registerrequest.json: The following command will register the user. ``` -# fabric-ca client register ../testdata/registerrequest.json http://localhost:8888 +# fabric-ca client register -config ../testdata/client-config.json ../testdata/registerrequest.json http://localhost:7054 ``` ### LDAP @@ -397,7 +414,7 @@ defaults timeout server 50000 listen http-in - bind *:8888 + bind *:7054 balance roundrobin server server1 server server2 diff --git a/cli/client/client.go b/cli/client/client.go index c9f5e4303..b227aff67 100644 --- a/cli/client/client.go +++ b/cli/client/client.go @@ -25,14 +25,18 @@ import ( _ "github.com/mattn/go-sqlite3" // import to support SQLite3 ) +var configFile string + // Command are the client commands func Command() error { - util.SetDefaultServerPort() cmds := map[string]*cli.Command{ "register": RegisterCommand, "enroll": EnrollCommand, "reenroll": ReenrollCommand, "revoke": RevokeCommand, } + + configFile = util.GetCommandLineOptValue("-config", true) + return cli.Start(cmds) } diff --git a/cli/client/client_test.go b/cli/client/client_test.go index 23601225e..68c03a2c8 100644 --- a/cli/client/client_test.go +++ b/cli/client/client_test.go @@ -20,28 +20,38 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" + "path" "testing" "time" - "github.com/cloudflare/cfssl/cli" "github.com/hyperledger/fabric-ca/cli/server" - "github.com/hyperledger/fabric-ca/util" ) var serverStarted bool var serverExitCode = 0 var dir string -const ( - ClientTLSConfig = "client-config.json" - FabricCADB = "../../testdata/fabric-ca.db" - CFGFile = "testconfig.json" +var ( + tdDir = "../../testdata" + cfgFile = "testconfig.json" + fabricCADB = path.Join(tdDir, "fabric-ca.db") + clientConfig = path.Join(tdDir, "client-config.json") + rrFile = path.Join(tdDir, "registerrequest.json") ) -// TestNewClient tests constructing a client +// TestNewClient tests constructing a client with a client config provided func TestNewClient(t *testing.T) { - _, err := NewClient(util.GetServerURL()) + loadMyIdentity := true + _, _, err := loadClient(loadMyIdentity, clientConfig) + if err != nil { + t.Errorf("Failed to create a client: %s", err) + } +} + +// TestNewClient tests constructing a client without a client config provided, will use default values +func TestNewClientNoConfig(t *testing.T) { + loadMyIdentity := true + _, _, err := loadClient(loadMyIdentity, "") if err != nil { t.Errorf("Failed to create a client: %s", err) } @@ -50,14 +60,9 @@ func TestNewClient(t *testing.T) { func TestEnrollCLI(t *testing.T) { startServer() - clientConfig := filepath.Join(dir, ClientTLSConfig) - os.Link("../../testdata/client-config2.json", clientConfig) + os.Args = []string{"client", "enroll", "-config", clientConfig, "admin", "adminpw"} - c := new(cli.Config) - - args := []string{"admin", "adminpw", util.GetServerURL()} - - err := enrollMain(args, *c) + err := Command() if err != nil { t.Error("Failed to register, err: ", err) } @@ -65,11 +70,10 @@ func TestEnrollCLI(t *testing.T) { } func TestReenrollCLI(t *testing.T) { - c := new(cli.Config) - args := []string{util.GetServerURL()} + os.Args = []string{"client", "reenroll", "-config", clientConfig} - err := reenrollMain(args, *c) + err := Command() if err != nil { t.Error("Failed to reenroll, err: ", err) } @@ -78,11 +82,9 @@ func TestReenrollCLI(t *testing.T) { func TestRegister(t *testing.T) { - c := new(cli.Config) - - args := []string{"../../testdata/registerrequest.json", util.GetServerURL()} + os.Args = []string{"client", "register", "-config", clientConfig, rrFile} - err := registerMain(args, *c) + err := Command() if err != nil { t.Error("Failed to register, err: ", err) } @@ -90,11 +92,10 @@ func TestRegister(t *testing.T) { } func TestRegisterNotEnoughArgs(t *testing.T) { - c := new(cli.Config) - args := []string{"../../testdata/registerrequest.json"} + os.Args = []string{"client", "register", "-config", clientConfig, rrFile} - err := registerMain(args, *c) + err := Command() if err == nil { t.Error("Should have failed, not enough arguments provided") } @@ -102,11 +103,10 @@ func TestRegisterNotEnoughArgs(t *testing.T) { } func TestRegisterNoJSON(t *testing.T) { - c := new(cli.Config) - args := []string{"", "admin", util.GetServerURL()} + os.Args = []string{"client", "register", "-config", clientConfig, "", "admin"} - err := registerMain(args, *c) + err := Command() if err == nil { t.Error("Should result in failure if registration json file not specificied, error: ", err) } @@ -114,11 +114,9 @@ func TestRegisterNoJSON(t *testing.T) { } func TestRegisterMissingRegistrar(t *testing.T) { - c := new(cli.Config) + os.Args = []string{"client", "register", "-config", clientConfig, "", ""} - args := []string{"", "", util.GetServerURL()} - - err := registerMain(args, *c) + err := Command() if err == nil { t.Error("Should result in failure if no registrar identity exists") } @@ -127,11 +125,9 @@ func TestRegisterMissingRegistrar(t *testing.T) { func TestRevoke(t *testing.T) { - c := new(cli.Config) + os.Args = []string{"client", "revoke", "-config", clientConfig, "admin"} - args := []string{util.GetServerURL(), "admin"} - - err := revokeMain(args, *c) + err := Command() if err != nil { t.Errorf("TestRevoke failed: %s", err) } @@ -140,11 +136,9 @@ func TestRevoke(t *testing.T) { func TestEnrollCLINotEnoughArgs(t *testing.T) { - c := new(cli.Config) - - args := []string{"testUser"} + os.Args = []string{"client", "enroll", "-config", clientConfig, "testUser"} - err := enrollMain(args, *c) + err := Command() if err == nil { t.Error("Should have failed, not enough argument provided") } @@ -153,11 +147,9 @@ func TestEnrollCLINotEnoughArgs(t *testing.T) { func TestEnrollCLIWithCSR(t *testing.T) { - c := new(cli.Config) - - args := []string{"notadmin", "pass", util.GetServerURL(), "../../testdata/csr.json"} + os.Args = []string{"client", "enroll", "-config", clientConfig, "notadmin", "pass", rrFile} - err := enrollMain(args, *c) + err := Command() if err != nil { t.Error("Failed to enroll, err: ", err) } @@ -166,11 +158,9 @@ func TestEnrollCLIWithCSR(t *testing.T) { func TestReenrollCLIWithCSR(t *testing.T) { - c := new(cli.Config) - - args := []string{util.GetServerURL(), "../../testdata/csr.json"} + os.Args = []string{"client", "reenroll", "-config", clientConfig, rrFile} - err := reenrollMain(args, *c) + err := Command() if err != nil { t.Error("Failed to reenroll, err: ", err) } @@ -178,11 +168,9 @@ func TestReenrollCLIWithCSR(t *testing.T) { func TestRevokeNoArg(t *testing.T) { - c := new(cli.Config) + os.Args = []string{"client", "revoke", "-config", clientConfig} - args := []string{util.GetServerURL()} - - err := revokeMain(args, *c) + err := Command() if err == nil { t.Error("TestRevokeNoArg succeeded but should have failed") } @@ -190,17 +178,33 @@ func TestRevokeNoArg(t *testing.T) { func TestRevokeNotAdmin(t *testing.T) { - c := new(cli.Config) + os.Args = []string{"client", "revoke", "-config", clientConfig, "admin"} - args := []string{util.GetServerURL(), "admin"} - - err := revokeMain(args, *c) + err := Command() if err == nil { t.Error("TestRevokeNotAdmin should have failed but didn't") } } +func TestIncompleteCommand(t *testing.T) { + os.Args = []string{"client"} + + err := Command() + if err == nil { + t.Error("Expected an error stating no command was given") + } +} + +func TestUnsupportedCommand(t *testing.T) { + os.Args = []string{"client", "unsupportedCMD"} + + err := Command() + if err == nil { + t.Error("Expected an error stating command is not defined") + } +} + func TestBogusCommand(t *testing.T) { err := Command() if err == nil { @@ -210,15 +214,15 @@ func TestBogusCommand(t *testing.T) { func TestLast(t *testing.T) { // Cleanup - os.Remove(FabricCADB) + os.Remove(fabricCADB) os.RemoveAll(dir) } func runServer() { os.Setenv("FABRIC_CA_DEBUG", "true") s := new(server.Server) - s.ConfigDir = "../../testdata" - s.ConfigFile = CFGFile + s.ConfigDir = tdDir + s.ConfigFile = cfgFile s.StartFromConfig = false s.Start() } @@ -232,7 +236,7 @@ func startServer() { } if !serverStarted { - os.Remove(FabricCADB) + os.Remove(fabricCADB) os.RemoveAll(dir) serverStarted = true fmt.Println("starting fabric-ca server ...") diff --git a/cli/client/enroll.go b/cli/client/enroll.go index 4f5232d3f..5ceca1412 100644 --- a/cli/client/enroll.go +++ b/cli/client/enroll.go @@ -29,7 +29,7 @@ var enrollUsageText = `fabric-ca client enroll -- Enroll with fabric-ca server Usage of client enroll command: Enroll a client and get an ecert: - fabric-ca client enroll ID SECRET FABRIC-CA-SERVER-ADDR + fabric-ca client enroll [-config config] ID SECRET FABRIC-CA-SERVER-ADDR Arguments: ID: Enrollment ID @@ -40,7 +40,7 @@ Arguments: Flags: ` -var enrollFlags = []string{} +var enrollFlags = []string{"config"} func enrollMain(args []string, c cli.Config) error { log.Debug("Entering cli/client/enrollMain") @@ -55,7 +55,8 @@ func enrollMain(args []string, c cli.Config) error { return err } - fcaServer, args, err := cli.PopFirstArgument(args) + loadMyIdentity := false + client, _, err := loadClient(loadMyIdentity, configFile) if err != nil { return err } @@ -65,11 +66,6 @@ func enrollMain(args []string, c cli.Config) error { Secret: secret, } - client, err := NewClient(fcaServer) - if err != nil { - return err - } - // Read the CSR JSON file if provided if len(args) > 0 { path, _, err2 := cli.PopFirstArgument(args) diff --git a/cli/client/reenroll.go b/cli/client/reenroll.go index cd0a5621f..a75f4a51f 100644 --- a/cli/client/reenroll.go +++ b/cli/client/reenroll.go @@ -28,7 +28,7 @@ import ( var reenrollUsageText = `fabric-ca client reenroll -- Reenroll with fabric-ca server Usage of client enroll command: - fabric-ca client reenroll FABRIC-CA-SERVER-ADDR + fabric-ca client reenroll [-config config] FABRIC-CA-SERVER-ADDR Arguments: FABRIC-CA-SERVER-ADDR: Fabric CA server address @@ -37,26 +37,17 @@ Arguments: Flags: ` -var reenrollFlags = []string{} +var reenrollFlags = []string{"config"} func reenrollMain(args []string, c cli.Config) error { log.Debug("Entering cli/client/reenrollMain") - fcaServer, args, err := cli.PopFirstArgument(args) + loadMyIdentity := true + client, id, err := loadClient(loadMyIdentity, configFile) if err != nil { return err } - client, err := NewClient(fcaServer) - if err != nil { - return err - } - - id, err := client.LoadMyIdentity() - if err != nil { - return fmt.Errorf("Client is not yet enrolled: %s", err) - } - req := &api.ReenrollmentRequest{} if len(args) > 0 { diff --git a/cli/client/register.go b/cli/client/register.go index ff731bd27..d11f1051e 100644 --- a/cli/client/register.go +++ b/cli/client/register.go @@ -30,7 +30,7 @@ var registerUsageText = `fabric-ca client register -- Register an ID with fabric Usage of client register command: Register a client with fabric-ca server: - fabric-ca client register REGISTER-REQUEST-FILE FABRIC-CA-SERVER-ADDR + fabric-ca client register [-config config] REGISTER-REQUEST-FILE FABRIC-CA-SERVER-ADDR Arguments: RRJSON: File contains registration info @@ -38,11 +38,11 @@ Arguments: Flags: ` -var registerFlags = []string{} +var registerFlags = []string{"config"} func registerMain(args []string, c cli.Config) error { - regFile, args, err := cli.PopFirstArgument(args) + regFile, _, err := cli.PopFirstArgument(args) if err != nil { return err } @@ -58,17 +58,8 @@ func registerMain(args []string, c cli.Config) error { return err } - fcaServer, _, err := cli.PopFirstArgument(args) - if err != nil { - return err - } - - client, err := NewClient(fcaServer) - if err != nil { - return err - } - - id, err := client.LoadMyIdentity() + loadMyIdentity := true + _, id, err := loadClient(loadMyIdentity, configFile) if err != nil { return err } diff --git a/cli/client/revoke.go b/cli/client/revoke.go index 743fd5721..0c0623679 100644 --- a/cli/client/revoke.go +++ b/cli/client/revoke.go @@ -28,7 +28,7 @@ var revokeUsageTxt = `fabric-ca client revoke -- revokes one or more certificate Usage: Revoke certificate(s): - fabric-ca client revoke FABRIC-CA-SERVER-URL [ENROLLMENT-ID] + fabric-ca client revoke [-config config] FABRIC-CA_SERVER_URL [ENROLLMENT_ID] Arguments: FABRIC-CA-SERVER-URL: The URL of the fabric-ca server @@ -37,14 +37,10 @@ Arguments: Flags: ` -var revokeFlags = []string{"aki", "serial", "reason"} +var revokeFlags = []string{"config", "aki", "serial", "reason"} func revokeMain(args []string, c cli.Config) error { - - fcaServer, args, err := cli.PopFirstArgument(args) - if err != nil { - return err - } + var err error var enrollmentID string if len(args) > 0 { @@ -54,12 +50,8 @@ func revokeMain(args []string, c cli.Config) error { } } - client, err := NewClient(fcaServer) - if err != nil { - return err - } - - id, err := client.LoadMyIdentity() + loadMyIdentity := true + _, id, err := loadClient(loadMyIdentity, configFile) if err != nil { return err } diff --git a/cli/client/util.go b/cli/client/util.go index 647648d26..1e5effea7 100644 --- a/cli/client/util.go +++ b/cli/client/util.go @@ -16,9 +16,34 @@ limitations under the License. package client -import "github.com/hyperledger/fabric-ca/lib" +import ( + "path" -// NewClient returns a client given a url -func NewClient(url string) (*lib.Client, error) { - return lib.NewClient(`{"serverURL":"` + url + `"}`) + "github.com/hyperledger/fabric-ca/lib" + + "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-ca/util" +) + +// LoadClient loads client configuration file +func loadClient(loadIdentity bool, configFile string) (*lib.Client, *lib.Identity, error) { + if configFile == "" { + configFile = path.Join(util.GetDefaultHomeDir(), "client-config.json") + } + log.Infof("Fabric-ca Client Configuration File: %s", configFile) + + client, err := lib.NewClient(configFile) + if err != nil { + return nil, nil, err + } + + if loadIdentity { + id, err2 := client.LoadMyIdentity() + if err != nil { + return nil, nil, err2 + } + return client, id, nil + } + + return client, nil, err } diff --git a/cli/server/config.go b/cli/server/config.go index b85d95745..195a0d35a 100644 --- a/cli/server/config.go +++ b/cli/server/config.go @@ -114,6 +114,14 @@ func configInit(cfg *cli.Config) { CFG.TLSConf.MutualTLSCAFile = abs(CFG.TLSConf.MutualTLSCAFile) absTLSClient(&CFG.TLSConf.DBClient) + if cfg.CAFile == "" { + cfg.CAFile = CFG.CAFile + } + + if cfg.KeyFile == "" { + cfg.CAKeyFile = CFG.CAKeyFile + } + if cfg.DBConfigFile == "" { cfg.DBConfigFile = cfg.ConfigFile } diff --git a/cli/server/init.go b/cli/server/init.go index 675bc65a7..6173f430d 100755 --- a/cli/server/init.go +++ b/cli/server/init.go @@ -18,7 +18,6 @@ package server import ( "encoding/json" - "errors" "io/ioutil" "path" @@ -26,6 +25,7 @@ import ( "github.com/cloudflare/cfssl/csr" "github.com/cloudflare/cfssl/initca" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-ca/util" "github.com/hyperledger/fabric/bccsp/factory" ) @@ -43,12 +43,12 @@ var initFlags = []string{"remote", "u"} func initMain(args []string, c cli.Config) (err error) { csrFile, _, err := cli.PopFirstArgument(args) if err != nil { - return errors.New(err.Error()) + return err } csrFileBytes, err := cli.ReadStdin(csrFile) if err != nil { - return errors.New(err.Error()) + return err } req := csr.CertificateRequest{ @@ -56,12 +56,12 @@ func initMain(args []string, c cli.Config) (err error) { } err = json.Unmarshal(csrFileBytes, &req) if err != nil { - return errors.New(err.Error()) + return err } bccsp, err := factory.GetDefault() if err != nil { - return errors.New(err.Error()) + return err } _ = bccsp //FIXME: replace the key generation and storage with BCCSP @@ -71,13 +71,12 @@ func initMain(args []string, c cli.Config) (err error) { var key, cert []byte cert, _, key, err = initca.New(&req) if err != nil { - return errors.New(err.Error()) + return err } - s := new(Server) - FCAHome, err := s.CreateHome() + FCAHome, err := util.CreateHome() if err != nil { - return errors.New(err.Error()) + return err } certerr := ioutil.WriteFile(path.Join(FCAHome, "server-cert.pem"), cert, 0755) if certerr != nil { diff --git a/cli/server/init_test.go b/cli/server/init_test.go index 853a4e1ff..4b5668cd5 100755 --- a/cli/server/init_test.go +++ b/cli/server/init_test.go @@ -28,12 +28,11 @@ import ( "github.com/cloudflare/cfssl/csr" "github.com/cloudflare/cfssl/initca" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-ca/util" ) func TestInitCA(t *testing.T) { - - s := new(Server) - FCAHome, err := s.CreateHome() + FCAHome, err := util.CreateHome() if err != nil { log.Fatalf("Failed to create fabric-ca home directory.") } diff --git a/cli/server/server.go b/cli/server/server.go index c461e52ab..0bfaee807 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -120,21 +120,6 @@ type Server struct { StartFromConfig bool } -// CreateHome will create a home directory if it does not exist -func (s *Server) CreateHome() (string, error) { - log.Debug("CreateHome") - home := util.GetDefaultHomeDir() - if _, err := os.Stat(home); err != nil { - if os.IsNotExist(err) { - err := os.MkdirAll(home, 0755) - if err != nil { - return "", err - } - } - } - return home, nil -} - // BootstrapDB loads the database based on config file func bootstrapDB() error { log.Debug("Bootstrap DB") @@ -152,12 +137,10 @@ func startMain(args []string, c cli.Config) error { log.Debug("server.startMain") var err error - s := new(Server) - home, err := s.CreateHome() + homeDir, err = util.CreateHome() if err != nil { return err } - homeDir = home configInit(&c) @@ -179,6 +162,7 @@ func startMain(args []string, c cli.Config) error { return err } + s := new(Server) return s.serverMain(args, c) } diff --git a/cli/server/server_test.go b/cli/server/server_test.go index 215c18086..7bc047314 100644 --- a/cli/server/server_test.go +++ b/cli/server/server_test.go @@ -21,7 +21,6 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "testing" "time" @@ -32,9 +31,9 @@ import ( ) const ( - CFGFile = "testconfig.json" - ClientTLSConfig = "client-config.json" - FCADB = "../../testdata/fabric-ca.db" + CFGFile = "testconfig.json" + ClientConfig = "../../testdata/client-config.json" + FCADB = "../../testdata/fabric-ca.db" ) var serverStarted bool @@ -87,9 +86,6 @@ func TestPostgresFail(t *testing.T) { func TestRegisterUser(t *testing.T) { startServer() - clientConfig := filepath.Join(dir, ClientTLSConfig) - os.Link("../../testdata/client-config2.json", clientConfig) - c := getClient(t) if c == nil { return @@ -216,8 +212,9 @@ func TestRevoke(t *testing.T) { } func TestGetTCerts(t *testing.T) { - c := getClient(t) - if c == nil { + c, err := lib.NewClient(ClientConfig) + if err != nil { + t.Errorf("TestGetTCerts.NewClient failed: %s", err) return } @@ -296,8 +293,9 @@ func TestEnroll(t *testing.T) { } func testUnregisteredUser(t *testing.T) { - c := getClient(t) - if c == nil { + c, err := lib.NewClient(ClientConfig) + if err != nil { + t.Errorf("Failed to create client [error: %s]", err) return } @@ -306,7 +304,7 @@ func testUnregisteredUser(t *testing.T) { Secret: "test", } - _, err := c.Enroll(req) + _, err = c.Enroll(req) if err == nil { t.Error("Unregistered user should not be allowed to enroll, should have failed") @@ -314,8 +312,9 @@ func testUnregisteredUser(t *testing.T) { } func testIncorrectToken(t *testing.T) { - c := getClient(t) - if c == nil { + c, err := lib.NewClient(ClientConfig) + if err != nil { + t.Errorf("Failed to create client [error: %s]", err) return } @@ -324,7 +323,7 @@ func testIncorrectToken(t *testing.T) { Secret: "pass1", } - _, err := c.Enroll(req) + _, err = c.Enroll(req) if err == nil { t.Error("Incorrect token should not be allowed to enroll, should have failed") @@ -332,14 +331,18 @@ func testIncorrectToken(t *testing.T) { } func testEnrollingUser(t *testing.T) { - c, _ := lib.NewClient("") + c, err := lib.NewClient(ClientConfig) + if err != nil { + t.Errorf("Failed to create client [error: %s]", err) + return + } req := &api.EnrollmentRequest{ Name: "testUser2", Secret: "user2", } - _, err := c.Enroll(req) + _, err = c.Enroll(req) if err != nil { t.Error("Enroll of user 'testUser2' with password 'user2' failed") @@ -366,9 +369,9 @@ func TestRevokeCertificatesByID(t *testing.T) { } func TestExpiration(t *testing.T) { - - c := getClient(t) - if c == nil { + c, err := lib.NewClient(ClientConfig) + if err != nil { + t.Errorf("Failed to create client [error: %s]", err) return } @@ -418,38 +421,6 @@ func TestUserRegistry(t *testing.T) { } -func TestCreateHome(t *testing.T) { - s := createServer() - t.Log("Test Creating Home Directory") - os.Unsetenv("FABRIC_CA_HOME") - tempDir, err := ioutil.TempDir("", "test") - if err != nil { - t.Errorf("Failed to create temp directory [error: %s]", err) - } - os.Setenv("HOME", tempDir) - - _, err = s.CreateHome() - if err != nil { - t.Errorf("Failed to create home directory, error: %s", err) - } - - if _, err = os.Stat(dir); err != nil { - if os.IsNotExist(err) { - t.Error("Failed to create home directory") - } - } - -} - -func TestMissingCertKey(t *testing.T) { - s := new(Server) - badConfig := "true" - err := s.Start(badConfig) - if err == nil { - t.Error("Server should not have started. No CA certificate or key were provided") - } -} - func TestLast(t *testing.T) { // Cleanup os.Remove(FCADB) @@ -476,7 +447,7 @@ func testWithoutAuthHdr(c *lib.Client, t *testing.T) { } func getClient(t *testing.T) *lib.Client { - c, err := lib.NewClient("") + c, err := lib.NewClient(ClientConfig) if err != nil { t.Fatalf("TestMisc.NewClient failed: %s", err) return nil diff --git a/factory_test.go b/factory_test.go index 82c0d2363..98bc04869 100644 --- a/factory_test.go +++ b/factory_test.go @@ -19,17 +19,11 @@ package main import "testing" // TestNewClient tests constructing a client +const clientConfig = "./testdata/client-config.json" + func TestNewClient(t *testing.T) { - _, err := NewClient(`{"serverAddr":"http://127.0.0.1:8888"}`) + _, err := NewClient(clientConfig) if err != nil { t.Errorf("Failed to create a client: %s", err) } } - -// TestNewClient tests constructing a client -func TestNewClientBadConfig(t *testing.T) { - _, err := NewClient("foobar") - if err == nil { - t.Error("TestNewClientBadConfig did not fail but should have") - } -} diff --git a/lib/client.go b/lib/client.go index e4c55eef9..f42c92e22 100644 --- a/lib/client.go +++ b/lib/client.go @@ -27,7 +27,6 @@ import ( "net/url" "os" "path" - "path/filepath" "strconv" "strings" @@ -45,18 +44,45 @@ const ( ) // NewClient is the constructor for the fabric-ca client API -func NewClient(config string) (*Client, error) { +func NewClient(configFile string) (*Client, error) { c := new(Client) // Set defaults - c.ServerURL = util.GetServerURL() - c.HomeDir = util.GetDefaultHomeDir() - if config != "" { - // Override any defaults - err := util.Unmarshal([]byte(config), c, "NewClient") - if err != nil { - return nil, err + if configFile != "" { + if _, err := os.Stat(configFile); err != nil { + log.Info("Fabric-ca client configuration file not found. Using Defaults...") + } else { + c.ConfigFile = configFile + var config []byte + var err error + config, err = ioutil.ReadFile(configFile) + if err != nil { + return nil, err + } + // Override any defaults + err = util.Unmarshal([]byte(config), c, "NewClient") + if err != nil { + return nil, err + } + } + } + + if c.ServerURL == "" { + c.ServerURL = util.GetServerURL() + } + + if c.HomeDir == "" { + c.HomeDir = util.GetDefaultHomeDir() + } + + if _, err := os.Stat(c.HomeDir); err != nil { + if os.IsNotExist(err) { + _, err := util.CreateHome() + if err != nil { + return nil, err + } } } + return c, nil } @@ -66,6 +92,8 @@ type Client struct { ServerURL string `json:"serverURL,omitempty"` // HomeDir is the home directory HomeDir string `json:"homeDir,omitempty"` + // ConfigFile is the location of the client configuration file + ConfigFile string } // Enroll enrolls a new identity @@ -259,7 +287,7 @@ func (c *Client) SendPost(req *http.Request) (interface{}, error) { reqStr := util.HTTPRequestToString(req) log.Debugf("Sending request\n%s", reqStr) - configFile, err := c.getClientConfig(c.HomeDir) + configFile, err := c.getClientConfig(c.ConfigFile) if err != nil { return nil, fmt.Errorf("Failed to load client config file [%s]; not sending\n%s", err, reqStr) } @@ -330,8 +358,8 @@ func (c *Client) getURL(endpoint string) (string, error) { func (c *Client) getClientConfig(path string) ([]byte, error) { log.Debug("Retrieving client config") - fcaClient := filepath.Join(path, clientConfigFile) - fileBytes, err := ioutil.ReadFile(fcaClient) + // fcaClient := filepath.Join(path, clientConfigFile) + fileBytes, err := ioutil.ReadFile(path) if err != nil { return nil, err } diff --git a/lib/client_test.go b/lib/client_test.go index 044241e1c..fc7d81946 100644 --- a/lib/client_test.go +++ b/lib/client_test.go @@ -22,20 +22,22 @@ import ( "io/ioutil" "net/http" "os" - "path/filepath" + "path" "testing" "time" "github.com/cloudflare/cfssl/log" "github.com/hyperledger/fabric-ca/api" "github.com/hyperledger/fabric-ca/cli/server" - "github.com/hyperledger/fabric-ca/util" ) -const ( - ClientTLSConfig = "client-config.json" - FCADB = "../testdata/fabric-ca.db" - CFGFile = "testconfig.json" +var ( + tdDir = "../testdata" + fcaDB = path.Join(tdDir, "fabric-ca.db") + cfgFile = path.Join(tdDir, "config.json") + testCfgFile = "testconfig.json" + clientConfig = path.Join(tdDir, "client-config2.json") + csrFile = path.Join(tdDir, "csr.json") ) var serverStarted bool @@ -45,9 +47,6 @@ var dir string func TestAllClient(t *testing.T) { startServer() - clientConfig := filepath.Join(dir, ClientTLSConfig) - os.Link("../testdata/client-config.json", clientConfig) - c := getClient() testRegister(c, t) @@ -189,7 +188,7 @@ func testRevocation(c *Client, t *testing.T, user, secret string, ecertOnly, sho } func testLoadCSRInfo(c *Client, t *testing.T) { - _, err := c.LoadCSRInfo("../testdata/csr.json") + _, err := c.LoadCSRInfo(csrFile) if err != nil { t.Errorf("testLoadCSRInfo failed: %s", err) } @@ -203,7 +202,7 @@ func testLoadNoCSRInfo(c *Client, t *testing.T) { } func testLoadBadCSRInfo(c *Client, t *testing.T) { - _, err := c.LoadCSRInfo("../testdata/config.json") + _, err := c.LoadCSRInfo(cfgFile) if err == nil { t.Error("testLoadBadCSRInfo passed but should have failed") } @@ -221,8 +220,7 @@ func TestSendBadPost(t *testing.T) { } func getClient() *Client { - fcaServer := fmt.Sprintf(`{"serverURL": "%s"}`, util.GetServerURL()) - c, err := NewClient(fcaServer) + c, err := NewClient(clientConfig) if err != nil { log.Errorf("getClient failed: %s", err) } @@ -238,7 +236,7 @@ func startServer() int { } if !serverStarted { - os.Remove(FCADB) + os.Remove(fcaDB) os.RemoveAll(dir) serverStarted = true fmt.Println("starting fabric-ca server ...") @@ -255,14 +253,14 @@ func runServer() { os.Setenv("FABRIC_CA_DEBUG", "true") os.Setenv("FABRIC_CA_HOME", dir) s := new(server.Server) - s.ConfigDir = "../testdata" - s.ConfigFile = CFGFile + s.ConfigDir = tdDir + s.ConfigFile = testCfgFile s.StartFromConfig = true s.Start() } func TestLast(t *testing.T) { // Cleanup - os.Remove(FCADB) + os.Remove(fcaDB) os.RemoveAll(dir) } diff --git a/lib/tls/tls.go b/lib/tls/tls.go index 1010c40fb..1283ba670 100644 --- a/lib/tls/tls.go +++ b/lib/tls/tls.go @@ -55,7 +55,7 @@ func GetClientTLSConfig(cfg *ClientTLSConfig) (*tls.Config, error) { caCertPool := x509.NewCertPool() if len(cfg.CACertFiles) == 0 { - log.Error("No CA cert files provided, TLS connection cannot be established") + log.Error("No CA cert files provided. If server requires TLS, connection will fail") } for _, cacert := range cfg.CACertFiles { diff --git a/lib/tls/tls_test.go b/lib/tls/tls_test.go index 80d5f8ba3..0acdbd61a 100644 --- a/lib/tls/tls_test.go +++ b/lib/tls/tls_test.go @@ -18,24 +18,24 @@ package tls import ( "encoding/json" - "fmt" "io/ioutil" "testing" ) +const clientConfig = "../../testdata/client-config.json" + func TestGetClientTLSConfig(t *testing.T) { - tlsConfig, err := ioutil.ReadFile("../../testdata/client-config2.json") + tlsConfig, err := ioutil.ReadFile(clientConfig) if err != nil { t.Errorf("Failed to read in TLS configuration file [error: %s]", err) } - var cfg ClientTLSConfig - json.Unmarshal(tlsConfig, &cfg) + var cfg = new(ClientTLSConfig) + json.Unmarshal(tlsConfig, cfg) - tls, err := GetClientTLSConfig(&cfg) + _, err = GetClientTLSConfig(cfg) if err != nil { t.Errorf("Failed to get TLS Config [error: %s]", err) } - fmt.Println("tls config: ", tls) } diff --git a/testdata/client-config.json b/testdata/client-config.json index 1b56e27a9..999aa9170 100644 --- a/testdata/client-config.json +++ b/testdata/client-config.json @@ -1,5 +1,8 @@ { -"ca_certfiles":["../testdata/root.pem"], -"client":{"keyfile":"../testdata/tls_client-key.pem", - "certfile":"../testdata/tls_client-cert.pem"} + "serverURL":"https://localhost:7054", + "ca_certfiles":["../../testdata/root.pem"], + "client":{ + "keyfile":"../../testdata/tls_client-key.pem", + "certfile":"../../testdata/tls_client-cert.pem" + } } diff --git a/testdata/client-config2.json b/testdata/client-config2.json index f5405b7ff..f57261500 100644 --- a/testdata/client-config2.json +++ b/testdata/client-config2.json @@ -1,5 +1,8 @@ { -"ca_certfiles":["../../testdata/root.pem"], -"client":{"keyfile":"../../testdata/tls_client-key.pem", - "certfile":"../../testdata/tls_client-cert.pem"} + "serverURL":"https://localhost:7054", + "ca_certfiles":["../testdata/root.pem"], + "client":{ + "keyfile":"../testdata/tls_client-key.pem", + "certfile":"../testdata/tls_client-cert.pem" + } } diff --git a/util/util.go b/util/util.go index 395a08645..b2ce1e6bd 100644 --- a/util/util.go +++ b/util/util.go @@ -40,6 +40,7 @@ import ( "strings" "time" + "github.com/cloudflare/cfssl/log" "github.com/jmoiron/sqlx" ) @@ -359,7 +360,31 @@ func HTTPResponseToString(resp *http.Response) string { resp.StatusCode, resp.Status, string(body)) } -// GetDefaultHomeDir returns the default fabric-ca home +// CreateHome will create a home directory if it does not exist +func CreateHome() (string, error) { + log.Debug("CreateHome") + home := os.Getenv("FABRIC_CA_HOME") + if home == "" { + home = os.Getenv("HOME") + if home != "" { + home = path.Join(home, "/fabric-cop") + } else { + home = "/var/hyperledger/fabric/dev/fabric-cop" + } + } + + if _, err := os.Stat(home); err != nil { + if os.IsNotExist(err) { + err := os.MkdirAll(home, 0755) + if err != nil { + return "", err + } + } + } + return home, nil +} + +// GetDefaultHomeDir returns the default fabric-cas home func GetDefaultHomeDir() string { home := os.Getenv("FABRIC_CA_HOME") if home == "" { diff --git a/util/util_test.go b/util/util_test.go index f73970016..a7bf525ef 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -140,6 +140,30 @@ func TestRemoveQuotesNone(t *testing.T) { } } +func TestCreateHome(t *testing.T) { + t.Log("Test Creating Home Directory") + os.Unsetenv("COP_HOME") + tempDir, err := ioutil.TempDir("", "test") + if err != nil { + t.Errorf("Failed to create temp directory [error: %s]", err) + } + os.Setenv("HOME", tempDir) + + _, err = CreateHome() + if err != nil { + t.Errorf("Failed to create home directory, error: %s", err) + } + + dir := filepath.Join(tempDir, "fabric-cop") + + if _, err = os.Stat(dir); err != nil { + if os.IsNotExist(err) { + t.Error("Failed to create home directory") + } + } + +} + func TestGetDefaultHomeDir(t *testing.T) { os.Setenv("FABRIC_CA_HOME", "") os.Setenv("HOME", "")