Skip to content

Commit df741bc

Browse files
author
Keith Smith
committed
Add support for dynamically registering a user with attributes
Signed-off-by: Keith Smith <[email protected]> Change-Id: I2d8883fbc19d99bb80815ec764d115af06e1be96
1 parent af6d3e8 commit df741bc

22 files changed

+508
-87
lines changed

core/chaincode/exectransaction_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func initMemSrvc() (net.Listener, error) {
6161
ca.CacheConfiguration() // Cache configuration
6262

6363
aca := ca.NewACA()
64-
eca := ca.NewECA()
64+
eca := ca.NewECA(aca)
6565
tca := ca.NewTCA(eca)
6666
tlsca := ca.NewTLSCA(eca)
6767

core/crypto/crypto_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1543,7 +1543,7 @@ func setup() {
15431543
func initPKI() {
15441544
ca.CacheConfiguration() // Need cache the configuration first
15451545
aca = ca.NewACA()
1546-
eca = ca.NewECA()
1546+
eca = ca.NewECA(aca)
15471547
tca = ca.NewTCA(eca)
15481548
tlsca = ca.NewTLSCA(eca)
15491549
}

docs/Setup/NodeSDK-setup.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,16 @@ function handleUserRequest(userName, chaincodeID, fcn, args) {
122122
// If this user has already been registered and/or enrolled, this will
123123
// still succeed because the state is kept in the KeyValStore
124124
// (i.e. in '/tmp/keyValStore' in this sample).
125+
// The attributes field is optional but can be used for role-based access control.
126+
// See fabric/sdk/node/test/unit/asset-mgmt-with-dynamic-roles.js as an example.
125127
var registrationRequest = {
126128
enrollmentID: userName,
127129
// Customize account & affiliation
128130
account: "bank_a",
129-
affiliation: "00001"
131+
affiliation: "00001",
132+
attributes: [
133+
{ name: "bankAccountId", value: "12345-67890" }
134+
]
130135
};
131136
chain.registerAndEnroll( registrationRequest, function(err, user) {
132137
if (err) return console.log("ERROR: %s",err);

examples/chaincode/go/asset_management/asset_management.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func (t *AssetManagementChaincode) Query(stub shim.ChaincodeStubInterface, funct
264264
myLogger.Debugf("Query [%s]", function)
265265

266266
if function != "query" {
267-
return nil, errors.New("Invalid query function name. Expecting \"query\"")
267+
return nil, errors.New("Invalid query function name. Expecting 'query' but found '" + function + "'")
268268
}
269269

270270
var err error

examples/chaincode/go/asset_management02/asset_management02_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ func initMembershipSrvc() {
531531
//ca.LogInit(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr, os.Stdout)
532532
ca.CacheConfiguration() // Cache configuration
533533
aca = ca.NewACA()
534-
eca = ca.NewECA()
534+
eca = ca.NewECA(aca)
535535
tca = ca.NewTCA(eca)
536536
tlsca = ca.NewTLSCA(eca)
537537

examples/chaincode/go/asset_management_with_roles/asset_management_with_roles.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (t *AssetManagementChaincode) assign(stub shim.ChaincodeStubInterface, args
113113

114114
callerRole, err := stub.ReadCertAttribute("role")
115115
if err != nil {
116-
fmt.Printf("Error reading attribute [%v] \n", err)
116+
fmt.Printf("Error reading attribute 'role' [%v] \n", err)
117117
return nil, fmt.Errorf("Failed fetching caller role. Error was [%v]", err)
118118
}
119119

@@ -235,7 +235,7 @@ func (t *AssetManagementChaincode) Invoke(stub shim.ChaincodeStubInterface, func
235235
// Query callback representing the query of a chaincode
236236
func (t *AssetManagementChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
237237
if function != "query" {
238-
return nil, errors.New("Invalid query function name. Expecting \"query\"")
238+
return nil, errors.New("Invalid query function name. Expecting 'query' but found '" + function + "'")
239239
}
240240

241241
var err error

examples/chaincode/go/asset_management_with_roles/asset_management_with_roles_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ func setup() {
366366
func initMembershipSrvc() {
367367
ca.CacheConfiguration() // Cache configuration
368368
aca = ca.NewACA()
369-
eca = ca.NewECA()
369+
eca = ca.NewECA(aca)
370370
tca = ca.NewTCA(eca)
371371
tlsca = ca.NewTLSCA(eca)
372372

examples/chaincode/go/rbac_tcerts_no_attrs/rbac_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ func setup() {
441441

442442
func initMemershipServices() {
443443
ca.CacheConfiguration() // Cache configuration
444-
eca = ca.NewECA()
444+
eca = ca.NewECA(nil)
445445
tca = ca.NewTCA(eca)
446446
tlsca = ca.NewTLSCA(eca)
447447

membersrvc/ca/aca.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ func (aca *ACA) fetchAttributes(id, affiliation string) ([]*AttributePair, error
276276
return attributes, nil
277277
}
278278

279-
func (aca *ACA) populateAttributes(attrs []*AttributePair) error {
279+
func (aca *ACA) PopulateAttributes(attrs []*AttributePair) error {
280+
281+
acaLogger.Debugf("PopulateAttributes: %+v", attrs)
282+
280283
mutex.Lock()
281284
defer mutex.Unlock()
282285

@@ -285,6 +288,7 @@ func (aca *ACA) populateAttributes(attrs []*AttributePair) error {
285288
return dberr
286289
}
287290
for _, attr := range attrs {
291+
acaLogger.Debugf("attr: %+v", attr)
288292
if err := aca.populateAttribute(tx, attr); err != nil {
289293
dberr = tx.Rollback()
290294
if dberr != nil {
@@ -331,7 +335,7 @@ func (aca *ACA) fetchAndPopulateAttributes(id, affiliation string) error {
331335
if err != nil {
332336
return err
333337
}
334-
err = aca.populateAttributes(attrs)
338+
err = aca.PopulateAttributes(attrs)
335339
if err != nil {
336340
return err
337341
}

membersrvc/ca/ca.go

+70-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import (
3535
"sync"
3636
"time"
3737

38+
gp "google/protobuf"
39+
3840
"github.com/hyperledger/fabric/core/crypto/primitives"
3941
"github.com/hyperledger/fabric/flogging"
4042
pb "github.com/hyperledger/fabric/membersrvc/protos"
@@ -578,11 +580,11 @@ func (ca *CA) validateAndGenerateEnrollID(id, affiliation string, role pb.Role)
578580

579581
// registerUser registers a new member with the CA
580582
//
581-
func (ca *CA) registerUser(id, affiliation string, role pb.Role, registrar, memberMetadata string, opt ...string) (string, error) {
583+
func (ca *CA) registerUser(id, affiliation string, role pb.Role, attrs []*pb.Attribute, aca *ACA, registrar, memberMetadata string, opt ...string) (string, error) {
582584
memberMetadata = removeQuotes(memberMetadata)
583585
roleStr, _ := MemberRoleToString(role)
584-
caLogger.Debugf("Received request to register user with id: %s, affiliation: %s, role: %s, registrar: %s, memberMetadata: %s\n",
585-
id, affiliation, roleStr, registrar, memberMetadata)
586+
caLogger.Debugf("Received request to register user with id: %s, affiliation: %s, role: %s, attrs: %+v, registrar: %s, memberMetadata: %s\n",
587+
id, affiliation, roleStr, attrs, registrar, memberMetadata)
586588

587589
var enrollID, tok string
588590
var err error
@@ -606,11 +608,21 @@ func (ca *CA) registerUser(id, affiliation string, role pb.Role, registrar, memb
606608
if err != nil {
607609
return "", err
608610
}
611+
609612
tok, err = ca.registerUserWithEnrollID(id, enrollID, role, memberMetadata, opt...)
610613
if err != nil {
611614
return "", err
612615
}
613-
return tok, nil
616+
617+
if attrs != nil && aca != nil {
618+
var pairs []*AttributePair
619+
pairs, err = toAttributePairs(id, affiliation, attrs)
620+
if err == nil {
621+
err = aca.PopulateAttributes(pairs)
622+
}
623+
}
624+
625+
return tok, err
614626
}
615627

616628
// registerUserWithEnrollID registers a new user and its enrollmentID, role and state
@@ -870,25 +882,36 @@ func (mm *MemberMetadata) canRegister(registrar string, newRole string, newMembe
870882
caLogger.Debugf("MM.canRegister: role %s can't be registered by %s\n", newRole, registrar)
871883
return errors.New("member " + registrar + " may not register member of type " + newRole)
872884
}
885+
873886
// The registrar privileges that are being registered must not be larger than the registrar's
874887
if newMemberMetadata == nil {
875888
// Not requesting registrar privileges for this member, so we are OK
876889
caLogger.Debug("MM.canRegister: not requesting registrar privileges")
877890
return nil
878891
}
879-
return strsContained(newMemberMetadata.Registrar.Roles, mm.Registrar.DelegateRoles, registrar, "delegateRoles")
892+
893+
// Make sure this registrar is not delegating an invalid role
894+
err := checkDelegateRoles(newMemberMetadata.Registrar.Roles, mm.Registrar.DelegateRoles, registrar)
895+
if err != nil {
896+
caLogger.Debug("MM.canRegister: checkDelegateRoles failure")
897+
return err
898+
}
899+
900+
// Can register OK
901+
caLogger.Debug("MM.canRegister: OK")
902+
return nil
880903
}
881904

882905
// Return an error if all strings in 'strs1' are not contained in 'strs2'
883-
func strsContained(strs1 []string, strs2 []string, registrar string, field string) error {
884-
caLogger.Debugf("CA.strsContained: registrar=%s, field=%s, strs1=%+v, strs2=%+v\n", registrar, field, strs1, strs2)
906+
func checkDelegateRoles(strs1 []string, strs2 []string, registrar string) error {
907+
caLogger.Debugf("CA.checkDelegateRoles: registrar=%s, strs1=%+v, strs2=%+v\n", registrar, strs1, strs2)
885908
for _, s := range strs1 {
886909
if !strContained(s, strs2) {
887-
caLogger.Debugf("CA.strsContained: no: %s not in %+v\n", s, strs2)
888-
return errors.New("user " + registrar + " may not register " + field + " " + s)
910+
caLogger.Debugf("CA.checkDelegateRoles: no: %s not in %+v\n", s, strs2)
911+
return errors.New("user " + registrar + " may not register delegateRoles " + s)
889912
}
890913
}
891-
caLogger.Debug("CA.strsContained: ok")
914+
caLogger.Debug("CA.checkDelegateRoles: ok")
892915
return nil
893916
}
894917

@@ -902,6 +925,16 @@ func strContained(str string, strs []string) bool {
902925
return false
903926
}
904927

928+
// Return true if 'str' is prefixed by any string in 'strs'; otherwise return false
929+
func isPrefixed(str string, strs []string) bool {
930+
for _, s := range strs {
931+
if strings.HasPrefix(str, s) {
932+
return true
933+
}
934+
}
935+
return false
936+
}
937+
905938
// convert a role to a string
906939
func role2String(role int) string {
907940
if role == int(pb.Role_CLIENT) {
@@ -928,3 +961,30 @@ func removeQuotes(str string) string {
928961
caLogger.Debugf("removeQuotes: %s\n", str)
929962
return str
930963
}
964+
965+
// Convert the protobuf array of attributes to the AttributePair array format
966+
// as required by the ACA code to populate the table
967+
func toAttributePairs(id, affiliation string, attrs []*pb.Attribute) ([]*AttributePair, error) {
968+
var pairs = make([]*AttributePair, 0)
969+
for _, attr := range attrs {
970+
vals := []string{id, affiliation, attr.Name, attr.Value, attr.NotBefore, attr.NotAfter}
971+
pair, err := NewAttributePair(vals, nil)
972+
if err != nil {
973+
return nil, err
974+
}
975+
pairs = append(pairs, pair)
976+
}
977+
caLogger.Debugf("toAttributePairs: id=%s, affiliation=%s, attrs=%v, pairs=%v\n",
978+
id, affiliation, attrs, pairs)
979+
return pairs, nil
980+
}
981+
982+
func convertTime(ts *gp.Timestamp) time.Time {
983+
var t time.Time
984+
if ts == nil {
985+
t = time.Unix(0, 0).UTC()
986+
} else {
987+
t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
988+
}
989+
return t
990+
}

membersrvc/ca/eca.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var (
4848
//
4949
type ECA struct {
5050
*CA
51+
aca *ACA
5152
obcKey []byte
5253
obcPriv, obcPub []byte
5354
gRPCServer *grpc.Server
@@ -59,8 +60,8 @@ func initializeECATables(db *sql.DB) error {
5960

6061
// NewECA sets up a new ECA.
6162
//
62-
func NewECA() *ECA {
63-
eca := &ECA{CA: NewCA("eca", initializeECATables)}
63+
func NewECA(aca *ACA) *ECA {
64+
eca := &ECA{CA: NewCA("eca", initializeECATables), aca: aca}
6465
flogging.LoggingInit("eca")
6566

6667
{
@@ -152,7 +153,7 @@ func (eca *ECA) populateUsersTable() {
152153
}
153154
}
154155
}
155-
eca.registerUser(id, affiliation, pb.Role(role), registrar, memberMetadata, vals[1])
156+
eca.registerUser(id, affiliation, pb.Role(role), nil, eca.aca, registrar, memberMetadata, vals[1])
156157
}
157158
}
158159

membersrvc/ca/ecaa.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (ecaa *ECAA) RegisterUser(ctx context.Context, in *pb.RegisterUserReq) (*pb
6060
}
6161
jsonStr := string(json)
6262
ecaaLogger.Debugf("gRPC ECAA:RegisterUser: json=%s", jsonStr)
63-
tok, err := ecaa.eca.registerUser(in.Id.Id, in.Affiliation, in.Role, registrarID, jsonStr)
63+
tok, err := ecaa.eca.registerUser(in.Id.Id, in.Affiliation, in.Role, in.Attributes, ecaa.eca.aca, registrarID, jsonStr)
6464

6565
// Return the one-time password
6666
return &pb.Token{Tok: []byte(tok)}, err
@@ -105,7 +105,7 @@ func (ecaa *ECAA) checkRegistrarSignature(in *pb.RegisterUserReq) error {
105105
// Check the signature
106106
if ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), hash.Sum(nil), r, s) == false {
107107
// Signature verification failure
108-
ecaaLogger.Debugf("ECAA.checkRegistrarSignature: failure for %s", registrar)
108+
ecaaLogger.Debugf("ECAA.checkRegistrarSignature: failure for %s (len=%d): %+v", registrar, len(raw), in)
109109
return errors.New("Signature verification failed.")
110110
}
111111

membersrvc/ca/membersrvc_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func setupTestConfig() {
7070
func initPKI() {
7171
CacheConfiguration() // Cache configuration
7272
aca = NewACA()
73-
eca = NewECA()
73+
eca = NewECA(aca)
7474
tca = NewTCA(eca)
7575
}
7676

membersrvc/ca/tca_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,17 @@ func initTCA() (*TCA, error) {
155155
}
156156

157157
CacheConfiguration() // Cache configuration
158-
eca := NewECA()
159-
if eca == nil {
160-
return nil, fmt.Errorf("Could not create a new ECA")
161-
}
162158

163159
aca := NewACA()
164160
if aca == nil {
165161
return nil, fmt.Errorf("Could not create a new ACA")
166162
}
167163

164+
eca := NewECA(aca)
165+
if eca == nil {
166+
return nil, fmt.Errorf("Could not create a new ECA")
167+
}
168+
168169
tca := NewTCA(eca)
169170
if tca == nil {
170171
return nil, fmt.Errorf("Could not create a new TCA")

membersrvc/ca/tlsca_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func TestTLS(t *testing.T) {
6363

6464
func startTLSCA(t *testing.T) {
6565
CacheConfiguration() // Cache configuration
66-
ecaS = NewECA()
66+
ecaS = NewECA(nil)
6767
tlscaS = NewTLSCA(ecaS)
6868

6969
var opts []grpc.ServerOption

0 commit comments

Comments
 (0)