From 8241d5c4094fa2dff8c1378685220f12866cb1f1 Mon Sep 17 00:00:00 2001 From: Aleksandar Likic Date: Wed, 14 Mar 2018 13:30:56 -0400 Subject: [PATCH] [FAB-8865] Clenaup pkg/client/msp References to low level packages are removed from MSP Client. Change-Id: I2214de6898301072175bd105f3760be558121cd1 Signed-off-by: Aleksandar Likic --- pkg/client/msp/api.go | 74 ++++++++++++++++ pkg/client/msp/msp.go | 87 ++++++++++++++++--- pkg/client/msp/msp_test.go | 15 ++-- pkg/client/msp/user.go | 93 +++++++++++++++++++++ test/integration/msp/enrollment_test.go | 13 ++- test/integration/msp/user_data_mgmt_test.go | 11 ++- 6 files changed, 261 insertions(+), 32 deletions(-) create mode 100644 pkg/client/msp/api.go create mode 100644 pkg/client/msp/user.go diff --git a/pkg/client/msp/api.go b/pkg/client/msp/api.go new file mode 100644 index 0000000000..c0ff2126cd --- /dev/null +++ b/pkg/client/msp/api.go @@ -0,0 +1,74 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package msp + +// AttributeRequest is a request for an attribute. +type AttributeRequest struct { + Name string + Optional bool +} + +// RegistrationRequest defines the attributes required to register a user with the CA +type RegistrationRequest struct { + // Name is the unique name of the identity + Name string + // Type of identity being registered (e.g. "peer, app, user") + Type string + // MaxEnrollments is the number of times the secret can be reused to enroll. + // if omitted, this defaults to max_enrollments configured on the server + MaxEnrollments int + // The identity's affiliation e.g. org1.department1 + Affiliation string + // Optional attributes associated with this identity + Attributes []Attribute + // CAName is the name of the CA to connect to + CAName string + // Secret is an optional password. If not specified, + // a random secret is generated. In both cases, the secret + // is returned from registration. + Secret string +} + +// Attribute defines additional attributes that may be passed along during registration +type Attribute struct { + Name string + Key string + Value string +} + +// RevocationRequest defines the attributes required to revoke credentials with the CA +type RevocationRequest struct { + // Name of the identity whose certificates should be revoked + // If this field is omitted, then Serial and AKI must be specified. + Name string + // Serial number of the certificate to be revoked + // If this is omitted, then Name must be specified + Serial string + // AKI (Authority Key Identifier) of the certificate to be revoked + AKI string + // Reason is the reason for revocation. See https://godoc.org/golang.org/x/crypto/ocsp + // for valid values. The default value is 0 (ocsp.Unspecified). + Reason string + // CAName is the name of the CA to connect to + CAName string +} + +// RevocationResponse represents response from the server for a revocation request +type RevocationResponse struct { + // RevokedCerts is an array of certificates that were revoked + RevokedCerts []RevokedCert + // CRL is PEM-encoded certificate revocation list (CRL) that contains all unexpired revoked certificates + CRL []byte +} + +// RevokedCert represents a revoked certificate +type RevokedCert struct { + // Serial number of the revoked certificate + Serial string + // AKI of the revoked certificate + AKI string +} diff --git a/pkg/client/msp/msp.go b/pkg/client/msp/msp.go index cf55b9159f..74959083b6 100644 --- a/pkg/client/msp/msp.go +++ b/pkg/client/msp/msp.go @@ -80,19 +80,44 @@ func newCAClient(ctx context.Client, orgName string) (mspapi.CAClient, error) { return caClient, nil } +// enrollmentOptions represent enrollment options +type enrollmentOptions struct { + secret string +} + +// EnrollmentOption describes a functional parameter for Enroll +type EnrollmentOption func(*enrollmentOptions) error + +// WithSecret enrollment option +func WithSecret(secret string) EnrollmentOption { + return func(o *enrollmentOptions) error { + o.secret = secret + return nil + } +} + // Enroll enrolls a registered user in order to receive a signed X509 certificate. // A new key pair is generated for the user. The private key and the // enrollment certificate issued by the CA are stored in SDK stores. // They can be retrieved by calling IdentityManager.GetSigningIdentity(). // // enrollmentID enrollment ID of a registered user -// enrollmentSecret secret associated with the enrollment ID -func (c *MSP) Enroll(enrollmentID string, enrollmentSecret string) error { +// opts represent enrollment options +func (c *MSP) Enroll(enrollmentID string, opts ...EnrollmentOption) error { + + eo := enrollmentOptions{} + for _, param := range opts { + err := param(&eo) + if err != nil { + return errors.WithMessage(err, "failed to enroll") + } + } + ca, err := newCAClient(c.ctx, c.orgName) if err != nil { return err } - return ca.Enroll(enrollmentID, enrollmentSecret) + return ca.Enroll(enrollmentID, eo.secret) } // Reenroll reenrolls an enrolled user in order to obtain a new signed X509 certificate @@ -107,36 +132,76 @@ func (c *MSP) Reenroll(enrollmentID string) error { // Register registers a User with the Fabric CA // request: Registration Request // Returns Enrolment Secret -func (c *MSP) Register(request *mspapi.RegistrationRequest) (string, error) { +func (c *MSP) Register(request *RegistrationRequest) (string, error) { ca, err := newCAClient(c.ctx, c.orgName) if err != nil { return "", err } - return ca.Register(request) + var a []mspapi.Attribute + for i := range request.Attributes { + a = append(a, mspapi.Attribute{Name: request.Attributes[i].Name, Key: request.Attributes[i].Key, Value: request.Attributes[i].Value}) + } + r := mspapi.RegistrationRequest{ + Name: request.Name, + Type: request.Type, + MaxEnrollments: request.MaxEnrollments, + Affiliation: request.Affiliation, + CAName: request.CAName, + Secret: request.Secret, + } + return ca.Register(&r) } // Revoke revokes a User with the Fabric CA // request: Revocation Request -func (c *MSP) Revoke(request *mspapi.RevocationRequest) (*mspapi.RevocationResponse, error) { +func (c *MSP) Revoke(request *RevocationRequest) (*RevocationResponse, error) { ca, err := newCAClient(c.ctx, c.orgName) if err != nil { return nil, err } - return ca.Revoke(request) + req := mspapi.RevocationRequest(*request) + resp, err := ca.Revoke(&req) + if err != nil { + return nil, err + } + var revokedCerts []RevokedCert + for i := range resp.RevokedCerts { + revokedCerts = append( + revokedCerts, + RevokedCert{ + Serial: resp.RevokedCerts[i].Serial, + AKI: resp.RevokedCerts[i].AKI, + }) + } + + return &RevocationResponse{ + RevokedCerts: revokedCerts, + CRL: resp.CRL, + }, nil } // GetSigningIdentity returns a signing identity for the given user name -func (c *MSP) GetSigningIdentity(userName string) (*mspctx.SigningIdentity, error) { +func (c *MSP) GetSigningIdentity(userName string) (*SigningIdentity, error) { user, err := c.GetUser(userName) if err != nil { + if err == mspctx.ErrUserNotFound { + return nil, ErrUserNotFound + } return nil, err } - signingIdentity := &mspctx.SigningIdentity{MspID: user.MspID(), PrivateKey: user.PrivateKey(), EnrollmentCert: user.EnrollmentCertificate()} + signingIdentity := &SigningIdentity{MspID: user.MspID(), PrivateKey: user.PrivateKey(), EnrollmentCert: user.EnrollmentCertificate()} return signingIdentity, nil } // GetUser returns a user for the given user name -func (c *MSP) GetUser(userName string) (mspctx.User, error) { +func (c *MSP) GetUser(userName string) (User, error) { im, _ := c.ctx.IdentityManager(c.orgName) - return im.GetUser(userName) + user, err := im.GetUser(userName) + if err != nil { + if err == mspctx.ErrUserNotFound { + return nil, ErrUserNotFound + } + return nil, err + } + return user, nil } diff --git a/pkg/client/msp/msp_test.go b/pkg/client/msp/msp_test.go index 539fde8b97..11556500b9 100644 --- a/pkg/client/msp/msp_test.go +++ b/pkg/client/msp/msp_test.go @@ -16,7 +16,6 @@ import ( "os" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" - mspctx "github.com/hyperledger/fabric-sdk-go/pkg/context/api/msp" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/hyperledger/fabric-sdk-go/pkg/msp/mocks" @@ -44,15 +43,15 @@ func TestMSP(t *testing.T) { } // Empty enrollment ID - err = msp.Enroll("", "user1") + err = msp.Enroll("", WithSecret("user1")) if err == nil { - t.Fatalf("Enroll shoudl return error for empty enrollment ID") + t.Fatalf("Enroll should return error for empty enrollment ID") } // Empty enrollment secret - err = msp.Enroll("enrolledUserName", "") + err = msp.Enroll("enrolledUserName", WithSecret("")) if err == nil { - t.Fatalf("Enroll shoudl return error for empty enrollment secret") + t.Fatalf("Enroll should return error for empty enrollment secret") } // Successful enrollment scenario @@ -60,11 +59,11 @@ func TestMSP(t *testing.T) { enrollUserName := randomUserName() _, err = msp.GetSigningIdentity(enrollUserName) - if err != mspctx.ErrUserNotFound { + if err != ErrUserNotFound { t.Fatalf("Expected to not find user") } - err = msp.Enroll(enrollUserName, "enrollmentSecret") + err = msp.Enroll(enrollUserName, WithSecret("enrollmentSecret")) if err != nil { t.Fatalf("Enroll return error %v", err) } @@ -110,7 +109,7 @@ func TestMSP(t *testing.T) { org2lUserName := randomUserName() - err = msp.Enroll(org2lUserName, "enrollmentSecret") + err = msp.Enroll(org2lUserName, WithSecret("enrollmentSecret")) if err != nil { t.Fatalf("Enroll return error %v", err) } diff --git a/pkg/client/msp/user.go b/pkg/client/msp/user.go new file mode 100644 index 0000000000..9ad41730e6 --- /dev/null +++ b/pkg/client/msp/user.go @@ -0,0 +1,93 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package msp + +import ( + "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" + "github.com/pkg/errors" +) + +var ( + // ErrUserNotFound indicates the user was not found + ErrUserNotFound = errors.New("user not found") +) + +// Identity supplies the serialized identity and key reference. +type Identity interface { + MspID() string + SerializedIdentity() ([]byte, error) + PrivateKey() core.Key +} + +// SigningIdentity is the identity object that encapsulates the user's private key for signing +// and the user's enrollment certificate (identity) +type SigningIdentity struct { + MspID string + EnrollmentCert []byte + PrivateKey core.Key +} + +// IdentityManager provides management of identities in a Fabric network +type IdentityManager interface { + GetSigningIdentity(name string) (*SigningIdentity, error) + GetUser(name string) (User, error) +} + +// User represents users that have been enrolled and represented by +// an enrollment certificate (ECert) and a signing key. The ECert must have +// been signed by one of the CAs the blockchain network has been configured to trust. +// An enrolled user (having a signing key and ECert) can conduct chaincode deployments, +// transactions and queries with the Chain. +// +// User ECerts can be obtained from a CA beforehand as part of deploying the application, +// or it can be obtained from the optional Fabric COP service via its enrollment process. +// +// Sometimes User identities are confused with Peer identities. User identities represent +// signing capability because it has access to the private key, while Peer identities in +// the context of the application/SDK only has the certificate for verifying signatures. +// An application cannot use the Peer identity to sign things because the application doesn’t +// have access to the Peer identity’s private key. +type User interface { + MspID() string + Name() string + SerializedIdentity() ([]byte, error) + PrivateKey() core.Key + EnrollmentCertificate() []byte +} + +// UserData is the representation of User in UserStore +// PrivateKey is stored separately, in the crypto store +type UserData struct { + Name string + MspID string + EnrollmentCertificate []byte +} + +// UserStore is responsible for UserData persistence +type UserStore interface { + Store(*UserData) error + Load(UserIdentifier) (*UserData, error) +} + +// UserIdentifier is the User's unique identifier +type UserIdentifier struct { + MspID string + Name string +} + +// PrivKeyKey is a composite key for accessing a private key in the key store +type PrivKeyKey struct { + MspID string + UserName string + SKI []byte +} + +// CertKey is a composite key for accessing a cert in the cert store +type CertKey struct { + MspID string + UserName string +} diff --git a/test/integration/msp/enrollment_test.go b/test/integration/msp/enrollment_test.go index d87d1884e8..3b403acf3c 100644 --- a/test/integration/msp/enrollment_test.go +++ b/test/integration/msp/enrollment_test.go @@ -13,7 +13,6 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" - "github.com/hyperledger/fabric-sdk-go/pkg/msp/api" "github.com/hyperledger/fabric-sdk-go/test/integration" ) @@ -44,7 +43,7 @@ func TestRegisterEnroll(t *testing.T) { // Get the MSP. // Without WithOrg option, uses default client organization. - msp, err := msp.New(ctxProvider) + mspClient, err := msp.New(ctxProvider) if err != nil { t.Fatalf("failed to create CA client: %v", err) } @@ -54,7 +53,7 @@ func TestRegisterEnroll(t *testing.T) { // CA operations that require the registrar's identity // will be rejected by the CA. registrarEnrollID, registrarEnrollSecret := getRegistrarEnrollmentCredentials(t, sdk.Config()) - err = msp.Enroll(registrarEnrollID, registrarEnrollSecret) + err = mspClient.Enroll(registrarEnrollID, msp.WithSecret(registrarEnrollSecret)) if err != nil { t.Fatalf("Enroll failed: %v", err) } @@ -70,7 +69,7 @@ func TestRegisterEnroll(t *testing.T) { userName := integration.GenerateRandomID() // Register the new user - enrollmentSecret, err := msp.Register(&api.RegistrationRequest{ + enrollmentSecret, err := mspClient.Register(&msp.RegistrationRequest{ Name: userName, Type: IdentityTypeUser, // Affiliation is mandatory. "org1" and "org2" are hardcoded as CA defaults @@ -82,19 +81,19 @@ func TestRegisterEnroll(t *testing.T) { } // Enroll the new user - err = msp.Enroll(userName, enrollmentSecret) + err = mspClient.Enroll(userName, msp.WithSecret(enrollmentSecret)) if err != nil { t.Fatalf("Enroll failed: %v", err) } // Get the new user's signing identity - _, err = msp.GetSigningIdentity(userName) + _, err = mspClient.GetSigningIdentity(userName) if err != nil { t.Fatalf("GetSigningIdentity failed: %v", err) } // Get the new user's full information - _, err = msp.GetUser(userName) + _, err = mspClient.GetUser(userName) if err != nil { t.Fatalf("GetSigningIdentity failed: %v", err) } diff --git a/test/integration/msp/user_data_mgmt_test.go b/test/integration/msp/user_data_mgmt_test.go index 40025cc6e6..6f58683089 100644 --- a/test/integration/msp/user_data_mgmt_test.go +++ b/test/integration/msp/user_data_mgmt_test.go @@ -20,7 +20,6 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/core/cryptosuite/bccsp/sw" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" mspimpl "github.com/hyperledger/fabric-sdk-go/pkg/msp" - "github.com/hyperledger/fabric-sdk-go/pkg/msp/api" "github.com/hyperledger/fabric-sdk-go/test/integration" ) @@ -82,7 +81,7 @@ func TestWithCustomStores(t *testing.T) { // Get the MSP. // Without WithOrg option, uses default client organization. - msp, err := msp.New(ctxProvider) + mspClient, err := msp.New(ctxProvider) if err != nil { t.Fatalf("failed to create MSP: %v", err) } @@ -92,7 +91,7 @@ func TestWithCustomStores(t *testing.T) { // CA operations that require the registrar's identity // will be rejected by the CA. registrarEnrollID, registrarEnrollSecret := getRegistrarEnrollmentCredentials(t, sdk.Config()) - err = msp.Enroll(registrarEnrollID, registrarEnrollSecret) + err = mspClient.Enroll(registrarEnrollID, msp.WithSecret(registrarEnrollSecret)) if err != nil { t.Fatalf("Enroll failed: %v", err) } @@ -101,7 +100,7 @@ func TestWithCustomStores(t *testing.T) { userName := integration.GenerateRandomID() // Register the new user - enrollmentSecret, err := msp.Register(&api.RegistrationRequest{ + enrollmentSecret, err := mspClient.Register(&msp.RegistrationRequest{ Name: userName, Type: IdentityTypeUser, // Affiliation is mandatory. "org1" and "org2" are hardcoded as CA defaults @@ -113,14 +112,14 @@ func TestWithCustomStores(t *testing.T) { } // Enroll the new user - err = msp.Enroll(userName, enrollmentSecret) + err = mspClient.Enroll(userName, msp.WithSecret(enrollmentSecret)) if err != nil { t.Fatalf("Enroll failed: %v", err) } // Let's try to find user's key and cert in our custom stores // and compare them to what is returned by msp.GetUser() - user, err := msp.GetUser(userName) + user, err := mspClient.GetUser(userName) if err != nil { t.Fatalf("GetUser failed: %v", err) }