Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 6b90b5f

Browse files
author
Aleksandar Likic
committed
[FAB-9471] Set attributes during user registration
Added support for setting attributes during user registration. Change-Id: I9c724245bf69d0a672b6ca0e8c2c0a83aee76ad9 Signed-off-by: Aleksandar Likic <aleksandar.likic@securekey.com>
1 parent c826ce1 commit 6b90b5f

File tree

10 files changed

+252
-22
lines changed

10 files changed

+252
-22
lines changed

internal/github.com/hyperledger/fabric/common/attrmgr/attrmgr.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ Please review third_party pinning scripts and patches for more details.
2626
package attrmgr
2727

2828
import (
29+
"crypto/x509"
30+
"crypto/x509/pkix"
2931
"encoding/asn1"
32+
"encoding/json"
33+
"fmt"
34+
35+
"github.com/pkg/errors"
3036
)
3137

3238
var (
@@ -53,10 +59,156 @@ type AttributeRequest interface {
5359
IsRequired() bool
5460
}
5561

62+
// New constructs an attribute manager
63+
func New() *Mgr { return &Mgr{} }
64+
5665
// Mgr is the attribute manager and is the main object for this package
5766
type Mgr struct{}
5867

68+
// ProcessAttributeRequestsForCert add attributes to an X509 certificate, given
69+
// attribute requests and attributes.
70+
func (mgr *Mgr) ProcessAttributeRequestsForCert(requests []AttributeRequest, attributes []Attribute, cert *x509.Certificate) error {
71+
attrs, err := mgr.ProcessAttributeRequests(requests, attributes)
72+
if err != nil {
73+
return err
74+
}
75+
return mgr.AddAttributesToCert(attrs, cert)
76+
}
77+
78+
// ProcessAttributeRequests takes an array of attribute requests and an identity's attributes
79+
// and returns an Attributes object containing the requested attributes.
80+
func (mgr *Mgr) ProcessAttributeRequests(requests []AttributeRequest, attributes []Attribute) (*Attributes, error) {
81+
attrsMap := map[string]string{}
82+
attrs := &Attributes{Attrs: attrsMap}
83+
missingRequiredAttrs := []string{}
84+
// For each of the attribute requests
85+
for _, req := range requests {
86+
// Get the attribute
87+
name := req.GetName()
88+
attr := getAttrByName(name, attributes)
89+
if attr == nil {
90+
if req.IsRequired() {
91+
// Didn't find attribute and it was required; return error below
92+
missingRequiredAttrs = append(missingRequiredAttrs, name)
93+
}
94+
// Skip attribute requests which aren't required
95+
continue
96+
}
97+
attrsMap[name] = attr.GetValue()
98+
}
99+
if len(missingRequiredAttrs) > 0 {
100+
return nil, errors.Errorf("The following required attributes are missing: %+v",
101+
missingRequiredAttrs)
102+
}
103+
return attrs, nil
104+
}
105+
106+
// AddAttributesToCert adds public attribute info to an X509 certificate.
107+
func (mgr *Mgr) AddAttributesToCert(attrs *Attributes, cert *x509.Certificate) error {
108+
buf, err := json.Marshal(attrs)
109+
if err != nil {
110+
return errors.Wrap(err, "Failed to marshal attributes")
111+
}
112+
ext := pkix.Extension{
113+
Id: AttrOID,
114+
Critical: false,
115+
Value: buf,
116+
}
117+
cert.Extensions = append(cert.Extensions, ext)
118+
return nil
119+
}
120+
121+
// GetAttributesFromCert gets the attributes from a certificate.
122+
func (mgr *Mgr) GetAttributesFromCert(cert *x509.Certificate) (*Attributes, error) {
123+
// Get certificate attributes from the certificate if it exists
124+
buf, err := getAttributesFromCert(cert)
125+
if err != nil {
126+
return nil, err
127+
}
128+
// Unmarshal into attributes object
129+
attrs := &Attributes{}
130+
if buf != nil {
131+
err := json.Unmarshal(buf, attrs)
132+
if err != nil {
133+
return nil, errors.Wrap(err, "Failed to unmarshal attributes from certificate")
134+
}
135+
}
136+
return attrs, nil
137+
}
138+
59139
// Attributes contains attribute names and values
60140
type Attributes struct {
61141
Attrs map[string]string `json:"attrs"`
62142
}
143+
144+
// Names returns the names of the attributes
145+
func (a *Attributes) Names() []string {
146+
i := 0
147+
names := make([]string, len(a.Attrs))
148+
for name := range a.Attrs {
149+
names[i] = name
150+
i++
151+
}
152+
return names
153+
}
154+
155+
// Contains returns true if the named attribute is found
156+
func (a *Attributes) Contains(name string) bool {
157+
_, ok := a.Attrs[name]
158+
return ok
159+
}
160+
161+
// Value returns an attribute's value
162+
func (a *Attributes) Value(name string) (string, bool, error) {
163+
attr, ok := a.Attrs[name]
164+
return attr, ok, nil
165+
}
166+
167+
// True returns nil if the value of attribute 'name' is true;
168+
// otherwise, an appropriate error is returned.
169+
func (a *Attributes) True(name string) error {
170+
val, ok, err := a.Value(name)
171+
if err != nil {
172+
return err
173+
}
174+
if !ok {
175+
return fmt.Errorf("Attribute '%s' was not found", name)
176+
}
177+
if val != "true" {
178+
return fmt.Errorf("Attribute '%s' is not true", name)
179+
}
180+
return nil
181+
}
182+
183+
// Get the attribute info from a certificate extension, or return nil if not found
184+
func getAttributesFromCert(cert *x509.Certificate) ([]byte, error) {
185+
for _, ext := range cert.Extensions {
186+
if isAttrOID(ext.Id) {
187+
return ext.Value, nil
188+
}
189+
}
190+
return nil, nil
191+
}
192+
193+
// Is the object ID equal to the attribute info object ID?
194+
func isAttrOID(oid asn1.ObjectIdentifier) bool {
195+
if len(oid) != len(AttrOID) {
196+
return false
197+
}
198+
for idx, val := range oid {
199+
if val != AttrOID[idx] {
200+
return false
201+
}
202+
}
203+
return true
204+
}
205+
206+
// Get an attribute from 'attrs' by its name, or nil if not found
207+
func getAttrByName(name string, attrs []Attribute) Attribute {
208+
for _, attr := range attrs {
209+
if attr.GetName() == name {
210+
return attr
211+
}
212+
}
213+
return nil
214+
}

pkg/client/msp/ca.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ type RegistrationRequest struct {
3636
// Attribute defines additional attributes that may be passed along during registration
3737
type Attribute struct {
3838
Name string
39-
Key string
4039
Value string
40+
ECert bool
4141
}
4242

4343
// RevocationRequest defines the attributes required to revoke credentials with the CA

pkg/client/msp/client.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,17 @@ func (c *Client) Register(request *RegistrationRequest) (string, error) {
129129
return "", err
130130
}
131131

132-
// TODO - this code is unused.
133-
//var a []mspapi.Attribute
134-
//for i := range request.Attributes {
135-
// a = append(a, mspapi.Attribute{Name: request.Attributes[i].Name, Key: request.Attributes[i].Key, Value: request.Attributes[i].Value})
136-
//}
132+
var a []mspapi.Attribute
133+
for i := range request.Attributes {
134+
a = append(a, mspapi.Attribute{Name: request.Attributes[i].Name, Value: request.Attributes[i].Value, ECert: request.Attributes[i].ECert})
135+
}
137136

138137
r := mspapi.RegistrationRequest{
139138
Name: request.Name,
140139
Type: request.Type,
141140
MaxEnrollments: request.MaxEnrollments,
142141
Affiliation: request.Affiliation,
142+
Attributes: a,
143143
CAName: request.CAName,
144144
Secret: request.Secret,
145145
}

pkg/msp/api/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ type RegistrationRequest struct {
5353
// Attribute defines additional attributes that may be passed along during registration
5454
type Attribute struct {
5555
Name string
56-
Key string
5756
Value string
57+
ECert bool
5858
}
5959

6060
// RevocationRequest defines the attributes required to revoke credentials with the CA

pkg/msp/caclient_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ func TestRegister(t *testing.T) {
192192

193193
// Register with valid request
194194
var attributes []api.Attribute
195-
attributes = append(attributes, api.Attribute{Key: "test1", Value: "test2"})
196-
attributes = append(attributes, api.Attribute{Key: "test2", Value: "test3"})
195+
attributes = append(attributes, api.Attribute{Name: "test1", Value: "test2"})
196+
attributes = append(attributes, api.Attribute{Name: "test2", Value: "test3"})
197197
secret, err := f.caClient.Register(&api.RegistrationRequest{Name: "test", Affiliation: "test", Attributes: attributes})
198198
if err != nil {
199199
t.Fatalf("identityManager Register return error %v", err)
@@ -217,8 +217,8 @@ func TestEmbeddedRegistar(t *testing.T) {
217217

218218
// Register with valid request
219219
var attributes []api.Attribute
220-
attributes = append(attributes, api.Attribute{Key: "test1", Value: "test2"})
221-
attributes = append(attributes, api.Attribute{Key: "test2", Value: "test3"})
220+
attributes = append(attributes, api.Attribute{Name: "test1", Value: "test2"})
221+
attributes = append(attributes, api.Attribute{Name: "test2", Value: "test3"})
222222
secret, err := f.caClient.Register(&api.RegistrationRequest{Name: "withEmbeddedRegistrar", Affiliation: "test", Attributes: attributes})
223223
if err != nil {
224224
t.Fatalf("identityManager Register return error %v", err)
@@ -254,8 +254,8 @@ func TestRegisterNoRegistrar(t *testing.T) {
254254

255255
// Register with valid request
256256
var attributes []api.Attribute
257-
attributes = append(attributes, api.Attribute{Key: "test1", Value: "test2"})
258-
attributes = append(attributes, api.Attribute{Key: "test2", Value: "test3"})
257+
attributes = append(attributes, api.Attribute{Name: "test1", Value: "test2"})
258+
attributes = append(attributes, api.Attribute{Name: "test2", Value: "test3"})
259259
_, err = f.caClient.Register(&api.RegistrationRequest{Name: "test", Affiliation: "test", Attributes: attributes})
260260
if err != api.ErrCARegistrarNotFound {
261261
t.Fatalf("Expected ErrCARegistrarNotFound, got: %v", err)

pkg/msp/fabcaadapter.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ func (c *fabricCAAdapter) Register(key core.Key, cert []byte, request *api.Regis
8787
// Contruct request for Fabric CA client
8888
var attributes []caapi.Attribute
8989
for i := range request.Attributes {
90-
attributes = append(attributes, caapi.Attribute{Name: request.
91-
Attributes[i].Key, Value: request.Attributes[i].Value})
90+
attributes = append(attributes, caapi.Attribute{Name: request.Attributes[i].Name, Value: request.Attributes[i].Value, ECert: request.Attributes[i].ECert})
9291
}
9392
var req = caapi.RegistrationRequest{
9493
CAName: request.CAName,

scripts/third_party_pins/fabric/apply_fabric_client_utils.sh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,6 @@ gofilter
180180
sed -i'' -e 's/&bccsp.SHA256Opts{}/factory.GetSHA256Opts()/g' "${TMP_PROJECT_PATH}/${FILTER_FILENAME}"
181181
sed -i'' -e 's/"github.com\/hyperledger\/fabric\/bccsp\/factory"/factory "github.com\/hyperledger\/fabric-sdk-go\/internal\/github.com\/hyperledger\/fabric\/sdkpatch\/cryptosuitebridge"/g' "${TMP_PROJECT_PATH}/${FILTER_FILENAME}"
182182

183-
FILTER_FILENAME="common/attrmgr/attrmgr.go"
184-
FILTER_FN=
185-
gofilter
186-
187183
FILTER_FILENAME="common/channelconfig/applicationorg.go"
188184
FILTER_FN=
189185
gofilter
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// +build !prev
2+
3+
/*
4+
Copyright SecureKey Technologies Inc. All Rights Reserved.
5+
6+
SPDX-License-Identifier: Apache-2.0
7+
*/
8+
9+
package msp
10+
11+
import (
12+
"testing"
13+
14+
"crypto/x509"
15+
"encoding/pem"
16+
17+
"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/attrmgr"
18+
"github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
19+
"github.com/stretchr/testify/assert"
20+
)
21+
22+
func checkCertAttributes(t *testing.T, certBytes []byte, expected []msp.Attribute) {
23+
decoded, _ := pem.Decode(certBytes)
24+
if decoded == nil {
25+
t.Fatalf("Failed cert decoding")
26+
}
27+
cert, err := x509.ParseCertificate(decoded.Bytes)
28+
if err != nil {
29+
t.Fatalf("failed to parse certificate: %v", err)
30+
}
31+
if cert == nil {
32+
t.Fatalf("failed to parse certificate: %v", err)
33+
}
34+
mgr := attrmgr.New()
35+
attrs, err := mgr.GetAttributesFromCert(cert)
36+
if err != nil {
37+
t.Fatalf("Failed to GetAttributesFromCert: %s", err)
38+
}
39+
for _, a := range expected {
40+
v, ok, err := attrs.Value(a.Name)
41+
assert.NoError(t, err)
42+
assert.True(t, attrs.Contains(a.Name), "does not contain attribute '%s'", a.Name)
43+
assert.True(t, ok, "attribute '%s' was not found", a.Name)
44+
assert.True(t, v == a.Value, "incorrect value for '%s'; expected '%s' but found '%s'", a.Name, a.Value, v)
45+
}
46+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// +build prev
2+
3+
/*
4+
Copyright SecureKey Technologies Inc. All Rights Reserved.
5+
6+
SPDX-License-Identifier: Apache-2.0
7+
*/
8+
9+
package msp
10+
11+
import (
12+
"testing"
13+
14+
"github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
15+
)
16+
17+
func checkCertAttributes(t *testing.T, certBytes []byte, expected []msp.Attribute) {
18+
// Do nothing, as the previous CA version wasn't setting attributes in generated certs.
19+
}

test/integration/msp/enrollment_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package msp
99
import (
1010
"testing"
1111

12+
"fmt"
13+
1214
"github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
1315
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
1416
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
@@ -62,10 +64,24 @@ func TestRegisterEnroll(t *testing.T) {
6264
// Generate a random user name
6365
username := integration.GenerateRandomID()
6466

67+
testAttributes := []msp.Attribute{
68+
msp.Attribute{
69+
Name: integration.GenerateRandomID(),
70+
Value: fmt.Sprintf("%s:ecert", integration.GenerateRandomID()),
71+
ECert: true,
72+
},
73+
msp.Attribute{
74+
Name: integration.GenerateRandomID(),
75+
Value: fmt.Sprintf("%s:ecert", integration.GenerateRandomID()),
76+
ECert: true,
77+
},
78+
}
79+
6580
// Register the new user
6681
enrollmentSecret, err := mspClient.Register(&msp.RegistrationRequest{
67-
Name: username,
68-
Type: IdentityTypeUser,
82+
Name: username,
83+
Type: IdentityTypeUser,
84+
Attributes: testAttributes,
6985
// Affiliation is mandatory. "org1" and "org2" are hardcoded as CA defaults
7086
// See https://github.com/hyperledger/fabric-ca/blob/release/cmd/fabric-ca-server/config.go
7187
Affiliation: "org2",
@@ -81,11 +97,13 @@ func TestRegisterEnroll(t *testing.T) {
8197
}
8298

8399
// Get the new user's signing identity
84-
_, err = mspClient.GetSigningIdentity(username)
100+
si, err := mspClient.GetSigningIdentity(username)
85101
if err != nil {
86102
t.Fatalf("GetSigningIdentity failed: %v", err)
87103
}
88104

105+
checkCertAttributes(t, si.EnrollmentCertificate(), testAttributes)
106+
89107
}
90108

91109
func getRegistrarEnrollmentCredentials(t *testing.T, ctxProvider context.ClientProvider) (string, string) {

0 commit comments

Comments
 (0)