Skip to content

Commit

Permalink
[FABG-883] Multiple CAs per organization
Browse files Browse the repository at this point in the history
Added abbility to handle multiple CA instances
associated with a single organization.

===================
WARNING: API Change
===================
Interface pkg/common/providers/msp/IdentityConfig has been changed.
In prior SDK versions, the CA info lookup methods used organization
as the lookup key, which implied one-to-one mapping between org
and CA instance. The interface has been changed to use CA instance
name as the lookup key.

Change-Id: I822d19dda63203ef2af13b0e7e5f524affd3f8ff
Signed-off-by: Aleksandar Likic <[email protected]>
  • Loading branch information
Aleksandar Likic committed Jul 24, 2019
1 parent 4db5c82 commit e0aa15e
Show file tree
Hide file tree
Showing 17 changed files with 390 additions and 159 deletions.
87 changes: 58 additions & 29 deletions pkg/client/msp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package msp
import (
"fmt"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"

"strings"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
Expand All @@ -30,8 +32,9 @@ import (
// Client enables access to Client services
type Client struct {
orgName string
caName string
ctx context.Client
// Name of the CA server instance
caName string
ctx context.Client
}

// ClientOption describes a functional parameter for the New constructor
Expand All @@ -45,6 +48,14 @@ func WithOrg(orgName string) ClientOption {
}
}

// WithCAInstance option
func WithCAInstance(caInstanceName string) ClientOption {
return func(msp *Client) error {
msp.caName = caInstanceName
return nil
}
}

// opts allows the user to specify more advanced request options
type requestOptions struct {
CA string
Expand All @@ -53,7 +64,7 @@ type requestOptions struct {
// RequestOption func for each Opts argument
type RequestOption func(ctx context.Client, opts *requestOptions) error

// WithCA allows for specifying optional CA name
// WithCA allows for specifying optional CA name (within the CA server instance)
func WithCA(caname string) RequestOption {
return func(ctx context.Client, o *requestOptions) error {
o.CA = caname
Expand All @@ -74,9 +85,9 @@ func New(clientProvider context.ClientProvider, opts ...ClientOption) (*Client,
}

for _, param := range opts {
err := param(&msp)
if err != nil {
return nil, errors.WithMessage(err, "failed to create Client")
err1 := param(&msp)
if err1 != nil {
return nil, errors.WithMessage(err1, "failed to create Client")
}
}

Expand All @@ -87,22 +98,40 @@ func New(clientProvider context.ClientProvider, opts ...ClientOption) (*Client,
return nil, errors.New("organization is not provided")
}

caConfig, ok := ctx.IdentityConfig().CAConfig(msp.orgName)
if ok {
msp.caName = caConfig.CAName
}

networkConfig := ctx.EndpointConfig().NetworkConfig()
_, ok = networkConfig.Organizations[strings.ToLower(msp.orgName)]
org, ok := networkConfig.Organizations[strings.ToLower(msp.orgName)]
if !ok {
return nil, fmt.Errorf("non-existent organization: '%s'", msp.orgName)
}

if msp.caName == "" && len(org.CertificateAuthorities) > 0 {
// Default to the first CA in org, if it is defined
msp.caName = org.CertificateAuthorities[0]
}

err = veiifyOrgCA(org, msp.caName)
if err != nil {
return nil, err
}

return &msp, nil
}

func newCAClient(ctx context.Client, orgName string) (mspapi.CAClient, error) {
func veiifyOrgCA(org fab.OrganizationConfig, caName string) error {
if caName == "" {
return nil
}
for _, name := range org.CertificateAuthorities {
if caName == name {
return nil
}
}
return fmt.Errorf("ca: '%s' doesn't belong to organization: '%s'", caName, org.MSPID)
}

func newCAClient(ctx context.Client, orgName string, caName string) (mspapi.CAClient, error) {

caClient, err := msp.NewCAClient(orgName, ctx)
caClient, err := msp.NewCAClient(orgName, ctx, msp.WithCAInstance(caName))
if err != nil {
return nil, errors.WithMessage(err, "failed to create CA Client")
}
Expand Down Expand Up @@ -171,7 +200,7 @@ func WithAttributeRequests(attrReqs []*AttributeRequest) EnrollmentOption {
// Return identity info including the secret
func (c *Client) CreateIdentity(request *IdentityRequest) (*IdentityResponse, error) {

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -207,7 +236,7 @@ func (c *Client) CreateIdentity(request *IdentityRequest) (*IdentityResponse, er
// Return updated identity info
func (c *Client) ModifyIdentity(request *IdentityRequest) (*IdentityResponse, error) {

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -243,7 +272,7 @@ func (c *Client) ModifyIdentity(request *IdentityRequest) (*IdentityResponse, er
// Return removed identity info
func (c *Client) RemoveIdentity(request *RemoveIdentityRequest) (*IdentityResponse, error) {

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -275,7 +304,7 @@ func (c *Client) GetAllIdentities(options ...RequestOption) ([]*IdentityResponse
return nil, err
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -304,7 +333,7 @@ func (c *Client) GetIdentity(ID string, options ...RequestOption) (*IdentityResp
return nil, err
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -367,7 +396,7 @@ func (c *Client) Enroll(enrollmentID string, opts ...EnrollmentOption) error {
}
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return err
}
Expand Down Expand Up @@ -406,7 +435,7 @@ func (c *Client) Reenroll(enrollmentID string, opts ...EnrollmentOption) error {
}
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return err
}
Expand All @@ -433,7 +462,7 @@ func (c *Client) Reenroll(enrollmentID string, opts ...EnrollmentOption) error {
// Returns:
// enrolment secret
func (c *Client) Register(request *RegistrationRequest) (string, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -462,7 +491,7 @@ func (c *Client) Register(request *RegistrationRequest) (string, error) {
// Returns:
// revocation response
func (c *Client) Revoke(request *RevocationRequest) (*RevocationResponse, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand All @@ -489,7 +518,7 @@ func (c *Client) Revoke(request *RevocationRequest) (*RevocationResponse, error)

// GetCAInfo returns generic CA information
func (c *Client) GetCAInfo() (*GetCAInfoResponse, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -546,7 +575,7 @@ func (c *Client) GetAffiliation(affiliation string, options ...RequestOption) (*
return nil, err
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand All @@ -570,7 +599,7 @@ func (c *Client) GetAllAffiliations(options ...RequestOption) (*AffiliationRespo
return nil, err
}

ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand All @@ -588,7 +617,7 @@ func (c *Client) GetAllAffiliations(options ...RequestOption) (*AffiliationRespo

// AddAffiliation adds a new affiliation to the server
func (c *Client) AddAffiliation(request *AffiliationRequest) (*AffiliationResponse, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand All @@ -612,7 +641,7 @@ func (c *Client) AddAffiliation(request *AffiliationRequest) (*AffiliationRespon

// ModifyAffiliation renames an existing affiliation on the server
func (c *Client) ModifyAffiliation(request *ModifyAffiliationRequest) (*AffiliationResponse, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand All @@ -639,7 +668,7 @@ func (c *Client) ModifyAffiliation(request *ModifyAffiliationRequest) (*Affiliat

// RemoveAffiliation removes an existing affiliation from the server
func (c *Client) RemoveAffiliation(request *AffiliationRequest) (*AffiliationResponse, error) {
ca, err := newCAClient(c.ctx, c.orgName)
ca, err := newCAClient(c.ctx, c.orgName, c.caName)
if err != nil {
return nil, err
}
Expand Down
89 changes: 82 additions & 7 deletions pkg/client/msp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ type nwConfig struct {
CertificateAuthorities map[string]mspImpl.CAConfig
}

// TestMSP is a unit test for Client enrollment and re-enrollment scenarios
func TestMSP(t *testing.T) {
// TestEnroll is a unit test for Client enrollment and re-enrollment scenarios
func TestEnroll(t *testing.T) {

f := testFixture{}
sdk := f.setup()
Expand Down Expand Up @@ -92,9 +92,12 @@ func TestMSP(t *testing.T) {
// Try with a non-default org
testWithOrg2(t, ctxProvider)

// Try with another CA instance
testWithOrg1TLSCAInstance(t, ctxProvider)

}

func TestMSPWithProfile(t *testing.T) {
func TestEnrollWithProfile(t *testing.T) {
f := testFixture{}
sdk := f.setup()
defer sdk.Close()
Expand Down Expand Up @@ -124,7 +127,7 @@ func TestMSPWithProfile(t *testing.T) {
}
}

func TestMSPWithType(t *testing.T) {
func TestEnrollWithType(t *testing.T) {
f := testFixture{}
sdk := f.setup()
defer sdk.Close()
Expand All @@ -150,7 +153,7 @@ func TestMSPWithType(t *testing.T) {
}
}

func TestMSPWithLabel(t *testing.T) {
func TestEnrollWithLabel(t *testing.T) {
f := testFixture{}
sdk := f.setup()
defer sdk.Close()
Expand Down Expand Up @@ -180,7 +183,7 @@ func TestMSPWithLabel(t *testing.T) {
}
}

func TestMSPWithAttributeRequests(t *testing.T) {
func TestEnrollWithAttributeRequests(t *testing.T) {
f := testFixture{}
sdk := f.setup()
defer sdk.Close()
Expand Down Expand Up @@ -211,7 +214,7 @@ func TestMSPWithAttributeRequests(t *testing.T) {
}
}

func TestWithNonExistentOrganization(t *testing.T) {
func TestNewWithNonExistentOrganization(t *testing.T) {
// Instantiate the SDK
configPath := filepath.Join(metadata.GetProjectPath(), metadata.SDKConfigPath, configFile)
sdk, err := fabsdk.New(config.FromFile(configPath))
Expand All @@ -225,6 +228,48 @@ func TestWithNonExistentOrganization(t *testing.T) {

}

func TestNewWithOrg1CAInstance(t *testing.T) {
// Instantiate the SDK
configPath := filepath.Join(metadata.GetProjectPath(), metadata.SDKConfigPath, configFile)
sdk, err := fabsdk.New(config.FromFile(configPath))
if err != nil {
t.Fatalf("SDK init failed: %s", err)
}
_, err = New(sdk.Context(), WithOrg("Org1"), WithCAInstance("tlsca.org1.example.com"))
if err != nil {
t.Fatal("Should not have failed with existing CA instance")
}

}

func TestNewWithOrg2CAInstance(t *testing.T) {
// Instantiate the SDK
configPath := filepath.Join(metadata.GetProjectPath(), metadata.SDKConfigPath, configFile)
sdk, err := fabsdk.New(config.FromFile(configPath))
if err != nil {
t.Fatalf("SDK init failed: %s", err)
}
_, err = New(sdk.Context(), WithOrg("Org2"), WithCAInstance("ca.org2.example.com"))
if err != nil {
t.Fatal("Should not have failed with existing CA instance")
}

}

func TestNewWithCAInstanceFromAnotherOrg(t *testing.T) {
// Instantiate the SDK
configPath := filepath.Join(metadata.GetProjectPath(), metadata.SDKConfigPath, configFile)
sdk, err := fabsdk.New(config.FromFile(configPath))
if err != nil {
t.Fatalf("SDK init failed: %s", err)
}
_, err = New(sdk.Context(), WithOrg("Org1"), WithCAInstance("ca.org2.example.com"))
if err == nil {
t.Fatal("Should have failed with CA instance from another org")
}

}

// TestNewWithProviderError tests New with provider error
func TestNewWithProviderError(t *testing.T) {

Expand Down Expand Up @@ -419,6 +464,33 @@ func testWithOrg2(t *testing.T, ctxProvider contextApi.ClientProvider) {
}
}

func testWithOrg1TLSCAInstance(t *testing.T, ctxProvider contextApi.ClientProvider) {
msp, err := New(ctxProvider, WithOrg("Org1"), WithCAInstance("tlsca.org1.example.com"))
if err != nil {
t.Fatalf("failed to create CA client: %s", err)
}

org1lUsername := randomUsername()

err = msp.Enroll(org1lUsername, WithSecret("enrollmentSecret"))
if err != nil {
t.Fatalf("Enroll return error %s", err)
}

org2EnrolledUser, err := msp.GetSigningIdentity(org1lUsername)
if err != nil {
t.Fatal("Expected to find user")
}

if org2EnrolledUser.Identifier().ID != org1lUsername {
t.Fatal("Enrolled user name doesn't match")
}

if org2EnrolledUser.Identifier().MSPID != "Org1MSP" {
t.Fatal("Enrolled user mspID doesn't match")
}
}

func getEnrolledUser(t *testing.T, msp *Client) mspctx.SigningIdentity {
// Successful enrollment scenario

Expand Down Expand Up @@ -548,10 +620,13 @@ func getCustomBackend(currentBackends ...core.ConfigBackend) []core.ConfigBacken

ca1Config := networkConfig.CertificateAuthorities["ca.org1.example.com"]
ca1Config.URL = caServerURL
tlsca1Config := networkConfig.CertificateAuthorities["tlsca.org1.example.com"]
tlsca1Config.URL = caServerURL
ca2Config := networkConfig.CertificateAuthorities["ca.org2.example.com"]
ca2Config.URL = caServerURL

networkConfig.CertificateAuthorities["ca.org1.example.com"] = ca1Config
networkConfig.CertificateAuthorities["tlsca.org1.example.com"] = tlsca1Config
networkConfig.CertificateAuthorities["ca.org2.example.com"] = ca2Config
backendMap["certificateAuthorities"] = networkConfig.CertificateAuthorities

Expand Down
Loading

0 comments on commit e0aa15e

Please sign in to comment.