Skip to content

Commit

Permalink
Store COP enrollment artifacts in MSP friendly way
Browse files Browse the repository at this point in the history
Instead of storing all enrollment artifacts in a single
client.json file, store it in separate key and store files.
This is inline with MSP (Membership Service Provider) APIs.
The enrollment certificate is stored at `$COP_ENROLLMENT_DIR/cert.pem`
by default, but a different path can be specified by setting
the `COP_CERT_FILE` environment variable.
The enrollment key is stored at `$COP_ENROLLMENT_DIR/key.pem`
by default, but a different path can be specified by setting
the `COP_KEY_FILE` environment variable.
The default value of the `COP_ENROLLMENT_DIR` environment
variable is `$COP_HOME`.
The default value of the `COP_HOME` environment variable
is `$HOME/cop`.

See https://jira.hyperledger.org/browse/FAB-1353

Change-Id: I77625acefad9365a345dc107d94ddf6c86e877c1
Signed-off-by: Keith Smith <[email protected]>
  • Loading branch information
Keith Smith committed Jan 10, 2017
1 parent a5666ff commit a9ff4d4
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 145 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,15 @@ The following command gets an ecert for the admin user.
# ./cop client enroll admin adminpw http://localhost:8888
```

Note that this stores the enrollment material in the `$COP_HOME/client.json` file.
The enrollment certificate is stored at `$COP_ENROLLMENT_DIR/cert.pem` by default, but a different
path can be specified by setting the `COP_CERT_FILE` environment variable to an absolute path name or a path relative to the current working directory.

The enrollment key is stored at `$COP_ENROLLMENT_DIR/key.pem` by default, but a different
path can be specified by setting the `COP_KEY_FILE` environment variable to an absolute path name or a path relative to the current working directory.

The default value of the `COP_ENROLLMENT_DIR` environment variable is `$COP_HOME`.

The default value of the `COP_HOME` environment variable is `$HOME/.cop`.

### Reenroll

Expand All @@ -263,7 +271,7 @@ key is used to authenticate to the COP server.
# ./cop client reenroll ../testdata/csr.json http://localhost:8888
```

Note that this updates the enrollment material in the `$COP_HOME/client.json` file.
The enrollment certificate and enrollment key are stored in the same location as described in the previous section for the `enroll` command.

### Register a new user

Expand Down
3 changes: 2 additions & 1 deletion cli/client/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ func enrollMain(args []string, c cli.Config) error {
return fmt.Errorf("failed to store enrollment information: %s", err)
}

log.Infof("enrollment information was successfully stored in %s", client.GetMyIdentityFile())
log.Infof("enrollment information was successfully stored in %s and %s",
client.GetMyKeyFile(), client.GetMyCertFile())

return nil
}
Expand Down
3 changes: 2 additions & 1 deletion cli/client/reenroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ func reenrollMain(args []string, c cli.Config) error {
return err
}

log.Infof("enrollment information was successfully stored in %s", client.GetMyIdentityFile())
log.Infof("enrollment information was successfully stored in %s and %s",
client.GetMyKeyFile(), client.GetMyCertFile())

return nil
}
Expand Down
77 changes: 40 additions & 37 deletions cli/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@ import (
"testing"
"time"

factory "github.com/hyperledger/fabric-cop"
"github.com/hyperledger/fabric-cop/cli/server/dbutil"
"github.com/hyperledger/fabric-cop/cli/server/ldap"
"github.com/hyperledger/fabric-cop/idp"
"github.com/hyperledger/fabric-cop/lib"
"github.com/hyperledger/fabric-cop/util"
)

const (
Expand Down Expand Up @@ -88,21 +86,23 @@ func TestRegisterUser(t *testing.T) {
clientConfig := filepath.Join(dir, ClientTLSConfig)
os.Link("../../testdata/cop_client2.json", clientConfig)

copServer := `{"serverURL":"https://localhost:8888"}`
c, _ := lib.NewClient(copServer)
c := getClient(t)
if c == nil {
return
}

enrollReq := &idp.EnrollmentRequest{
Name: "admin",
Secret: "adminpw",
}

ID, err := c.Enroll(enrollReq)
id, err := c.Enroll(enrollReq)
if err != nil {
t.Error("Enroll of user 'admin' with password 'adminpw' failed")
return
}

err = ID.Store()
err = id.Store()
if err != nil {
t.Errorf("Failed to store enrollment information: %s", err)
return
Expand All @@ -114,14 +114,6 @@ func TestRegisterUser(t *testing.T) {
Group: "bank_a",
}

id, _ := factory.NewIdentity()
path := filepath.Join(dir, "client.json")
identity, err := ioutil.ReadFile(path)
if err != nil {
t.Error(err)
}
util.Unmarshal(identity, id, "identity")

regReq.Registrar = id

_, err = c.Register(regReq)
Expand All @@ -131,10 +123,8 @@ func TestRegisterUser(t *testing.T) {
}

func TestMisc(t *testing.T) {
copServer := `{"serverURL":"https://localhost:8888"}`
c, err := lib.NewClient(copServer)
if err != nil {
t.Errorf("TestMisc.NewClient failed: %s", err)
c := getClient(t)
if c == nil {
return
}
id, err := c.LoadMyIdentity()
Expand All @@ -152,8 +142,10 @@ func TestMisc(t *testing.T) {
}

func TestEnrollUser(t *testing.T) {
copServer := `{"serverURL":"https://localhost:8888"}`
c, _ := lib.NewClient(copServer)
c := getClient(t)
if c == nil {
return
}

req := &idp.EnrollmentRequest{
Name: "testUser",
Expand Down Expand Up @@ -184,8 +176,10 @@ func TestEnrollUser(t *testing.T) {
}

func TestRevoke(t *testing.T) {
copServer := `{"serverURL":"https://localhost:8888"}`
c, _ := lib.NewClient(copServer)
c := getClient(t)
if c == nil {
return
}

req := &idp.EnrollmentRequest{
Name: "admin2",
Expand Down Expand Up @@ -243,26 +237,25 @@ func TestGetTCerts(t *testing.T) {
}

func TestMaxEnrollment(t *testing.T) {
CFG.UsrReg.MaxEnrollments = 2

copServer := `{"serverURL":"https://localhost:8888"}`
c, _ := lib.NewClient(copServer)

regReq := &idp.RegistrationRequest{
Name: "MaxTestUser",
Type: "Client",
Group: "bank_a",
c := getClient(t)
if c == nil {
return
}

id, _ := factory.NewIdentity()
path := filepath.Join(dir, "client.json")
identity, err := ioutil.ReadFile(path)
id, err := c.LoadMyIdentity()
if err != nil {
t.Error(err)
t.Errorf("TestMisc.LoadMyIdentity failed: %s", err)
return
}
util.Unmarshal(identity, id, "identity")

regReq.Registrar = id
CFG.UsrReg.MaxEnrollments = 2

regReq := &idp.RegistrationRequest{
Name: "MaxTestUser",
Type: "Client",
Group: "bank_a",
Registrar: id,
}

resp, err := c.Register(regReq)
if err != nil {
Expand Down Expand Up @@ -485,3 +478,13 @@ func testWithoutAuthHdr(c *lib.Client, t *testing.T) {
t.Error("testWithAuthHdr.SendPost should have failed but passed")
}
}

func getClient(t *testing.T) *lib.Client {
copServer := `{"serverURL":"https://localhost:8888"}`
c, err := lib.NewClient(copServer)
if err != nil {
t.Fatalf("TestMisc.NewClient failed: %s", err)
return nil
}
return c
}
5 changes: 0 additions & 5 deletions factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,3 @@ import "github.com/hyperledger/fabric-cop/lib"
func NewClient(config string) (*lib.Client, error) {
return lib.NewClient(config)
}

// NewIdentity creates a new identity
func NewIdentity() (*lib.Identity, error) {
return new(lib.Identity), nil
}
7 changes: 0 additions & 7 deletions factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,3 @@ func TestNewClientBadConfig(t *testing.T) {
t.Error("TestNewClientBadConfig did not fail but should have")
}
}

func TestNewIdentity(t *testing.T) {
_, err := NewIdentity()
if err != nil {
t.Error("Failed to create identity")
}
}
9 changes: 0 additions & 9 deletions idp/idp.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ type ClientAPI interface {
// ImportSigner imports a signer from an external CA
// @param req The import request
ImportSigner(req *ImportSignerRequest) (Signer, error)

// DeserializeIdentity deserializes an identity
DeserializeIdentity([]byte) (Identity, error)
}

// PeerAPI is the API used by the peer pertaining to the IDP
Expand Down Expand Up @@ -87,9 +84,6 @@ type Identity interface {

// Delete this identity completely and revoke all of it's signers
Delete() error

// Serialize an identity
Serialize() ([]byte, error)
}

// TemporalSigner is a signer which can be renewed and revoked
Expand Down Expand Up @@ -135,9 +129,6 @@ type Verifier interface {

// VerifyAttributes verifies attributes given proofs
VerifyAttributes(proof [][]byte, spec *AttributeProofSpec) error

// Serialize verifier
Serialize() ([]byte, error)
}

// RegistrationRequest for a new identity
Expand Down
77 changes: 52 additions & 25 deletions lib/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func NewClient(config string) (*Client, error) {
// Set defaults
c.ServerURL = "http://localhost:8888"
c.HomeDir = util.GetDefaultHomeDir()
c.MyIDFile = "client.json"
if config != "" {
// Override any defaults
err := util.Unmarshal([]byte(config), c, "NewClient")
Expand All @@ -71,8 +70,6 @@ type Client struct {
ServerURL string `json:"serverURL,omitempty"`
// HomeDir is the home directory
HomeDir string `json:"homeDir,omitempty"`
// MyIDFIle is the name of ID file which gets loaded by LoadMyIdentity
MyIDFile string `json:"fileName,omitempty"`
}

// Capabilities returns the capabilities COP
Expand Down Expand Up @@ -101,16 +98,14 @@ func (c *Client) Register(req *idp.RegistrationRequest) (*idp.RegistrationRespon
if req.Registrar == nil {
return nil, errors.New("Register was called without a Registrar identity set")
}

var request cop.RegisterRequest
request.User = req.Name
request.Type = req.Type
request.Group = req.Group
request.Attributes = req.Attributes
request.CallerID = req.Registrar.GetName()

newID := newIdentity(c, req.Registrar.GetName(), req.Registrar.(*Identity).GetMyKey(), req.Registrar.(*Identity).GetMyCert())
req.Registrar = newID

reqBody, err := util.Marshal(request, "RegistrationRequest")
if err != nil {
return nil, err
Expand Down Expand Up @@ -285,25 +280,65 @@ func (c *Client) ImportSigner(req *idp.ImportSignerRequest) (idp.Signer, error)

// LoadMyIdentity loads the client's identity from disk
func (c *Client) LoadMyIdentity() (*Identity, error) {
myIDFile := c.GetMyIdentityFile()
if !util.FileExists(myIDFile) {
return nil, fmt.Errorf("client is not enrolled; '%s' is not an existing file", myIDFile)
return c.LoadIdentity(c.GetMyKeyFile(), c.GetMyCertFile())
}

// StoreMyIdentity stores my identity to disk
func (c *Client) StoreMyIdentity(key, cert []byte) error {
err := util.WriteFile(c.GetMyKeyFile(), key, 0600)
if err != nil {
return err
}
return util.WriteFile(c.GetMyCertFile(), cert, 0644)
}

// GetMyKeyFile returns the path to this identity's key file
func (c *Client) GetMyKeyFile() string {
file := os.Getenv("COP_KEY_FILE")
if file == "" {
file = path.Join(c.GetMyEnrollmentDir(), "key.pem")
}
return file
}

// GetMyCertFile returns the path to this identity's certificate file
func (c *Client) GetMyCertFile() string {
file := os.Getenv("COP_CERT_FILE")
if file == "" {
file = path.Join(c.GetMyEnrollmentDir(), "cert.pem")
}
return c.LoadIdentity(myIDFile)
return file
}

// GetMyIdentityFile returns the path to this identity's ID file
func (c *Client) GetMyIdentityFile() string {
return path.Join(c.HomeDir, c.MyIDFile)
// GetMyEnrollmentDir returns the path to this identity's enrollment directory
func (c *Client) GetMyEnrollmentDir() string {
dir := os.Getenv("COP_ENROLLMENT_DIR")
if dir == "" {
dir = c.HomeDir
}
return dir
}

// LoadIdentity loads an identity from a file on disk at path
func (c *Client) LoadIdentity(path string) (*Identity, error) {
buf, err := util.ReadFile(path)
// LoadIdentity loads an identity from disk
func (c *Client) LoadIdentity(keyFile, certFile string) (*Identity, error) {
key, err := util.ReadFile(keyFile)
if err != nil {
return nil, err
}
cert, err := util.ReadFile(certFile)
if err != nil {
return nil, err
}
return c.DeserializeIdentity(buf)
return c.NewIdentity(key, cert)
}

// NewIdentity creates a new identity
func (c *Client) NewIdentity(key, cert []byte) (*Identity, error) {
name, err := util.GetEnrollmentIDFromPEM(cert)
if err != nil {
return nil, err
}
return newIdentity(c, name, key, cert), nil
}

// LoadCSRInfo reads CSR (Certificate Signing Request) from a file
Expand All @@ -321,14 +356,6 @@ func (c *Client) LoadCSRInfo(path string) (*idp.CSRInfo, error) {
return &csrInfo, nil
}

// DeserializeIdentity deserializes an identity
func (c *Client) DeserializeIdentity(buf []byte) (*Identity, error) {
id := new(Identity)
err := util.Unmarshal(buf, id, "Identity")
id.client = c
return id, err
}

// NewPost create a new post request
func (c *Client) NewPost(endpoint string, reqBody []byte) (*http.Request, error) {
curl, cerr := c.getURL(endpoint)
Expand Down
18 changes: 0 additions & 18 deletions lib/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,24 +210,6 @@ func testCapabilities(c *Client, t *testing.T) {
}
}

func TestDeserializeIdentity(t *testing.T) {
config := `{"serverURL":"https://localhost:8888"}`
c, err := NewClient(config)
if err != nil {
t.Error("Failed to create client object")
}

idByte, err := ioutil.ReadFile("../testdata/client.json")
if err != nil {
t.Error("Error occured during reading of id file")
}

_, err = c.DeserializeIdentity(idByte)
if err != nil {
t.Error("Error occured during deserialization, error: ", err)
}
}

func TestSendBadPost(t *testing.T) {
c := new(Client)
curl := "fake"
Expand Down
Loading

0 comments on commit a9ff4d4

Please sign in to comment.