Skip to content

Commit 218a9bd

Browse files
Anil AmbatiKeith Smith
Anil Ambati
authored and
Keith Smith
committedOct 2, 2017
[FAB-6089] ABAC chaincode library
Added library API for Attribute-Based Access Control. See the README.md for an overall description. Change-Id: I487afc57c597a17c6baacf3987527eee8a0978c9 Signed-off-by: Anil Ambati <aambati@us.ibm.com> Signed-off-by: Keith Smith <bksmith@us.ibm.com>
1 parent 3a7f893 commit 218a9bd

File tree

5 files changed

+665
-1
lines changed

5 files changed

+665
-1
lines changed
 

‎core/chaincode/lib/cid/README.md

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Client Identity Chaincode Library
2+
3+
The client identity chaincode library enables you to write chaincode which
4+
makes access control decisions based on the identity of the client
5+
(i.e. the invoker of the chaincode). In particular, you may make access
6+
control decisions based on either or both of the following associated with
7+
the client:
8+
9+
* the client identity's MSP (Membership Service Provider) ID
10+
* an attribute associated with the client identity
11+
12+
Attributes are simply name and value pairs associated with an identity.
13+
For example, `email=me@gmail.com` indicates an identity has the `email`
14+
attribute with a value of `me@gmail.com`.
15+
16+
17+
## Using the client identity chaincode library
18+
19+
This section describes how to use the client identity chaincode library.
20+
21+
All code samples below assume two things:
22+
1. The type of the `stub` variable is `ChaincodeStubInterface` as passed
23+
to your chaincode.
24+
2. You have added the following import statement to your chaincode.
25+
```
26+
import "github.com/hyperledger/fabric/core/chaincode/lib/cid"
27+
```
28+
#### Getting the client's ID
29+
30+
The following demonstrates how to get an ID for the client which is guaranteed
31+
to be unique within the MSP:
32+
33+
```
34+
id, err := cid.GetID(stub)
35+
```
36+
37+
#### Getting the MSP ID
38+
39+
The following demonstrates how to get the MSP ID of the client's identity:
40+
41+
```
42+
mspid, err := cid.GetMSPID(stub)
43+
```
44+
45+
#### Getting an attribute value
46+
47+
The following demonstrates how to get the value of the *attr1* attribute:
48+
49+
```
50+
val, ok, err := cid.GetAttributeValue(stub, "attr1")
51+
if err != nil {
52+
// There was an error trying to retrieve the attribute
53+
}
54+
if !ok {
55+
// The client identity does not possess the attribute
56+
}
57+
// Do something with the value of 'val'
58+
```
59+
60+
#### Asserting an attribute value
61+
62+
Often all you want to do is to make an access control decision based on the value
63+
of an attribute, i.e. to assert the value of an attribute. For example, the following
64+
will return an error if the client does not have the `myapp.admin` attribute
65+
with a value of `true`:
66+
67+
```
68+
err := cid.AssertAttributeValue(stub, "myapp.admin", "true")
69+
if err != nil {
70+
// Return an error
71+
}
72+
```
73+
74+
This is effectively using attributes to implement role-based access control,
75+
or RBAC for short.
76+
77+
#### Getting the client's X509 certificate
78+
79+
The following demonstrates how to get the X509 certificate of the client, or
80+
nil if the client's identity was not based on an X509 certificate:
81+
82+
```
83+
cert, err := cid.GetX509Certificate(stub)
84+
```
85+
86+
Note that both `cert` and `err` may be nil as will be the case if the identity
87+
is not using an X509 certificate.
88+
89+
#### Performing multiple operations more efficiently
90+
91+
Sometimes you may need to perform multiple operations in order to make an access
92+
decision. For example, the following demonstrates how to grant access to
93+
identities with MSP *org1MSP* and *attr1* OR with MSP *org1MSP* and *attr2*.
94+
95+
```
96+
// Get the client ID object
97+
id, err := cid.New(stub)
98+
if err != nil {
99+
// Handle error
100+
}
101+
mspid, err := id.GetMSPID()
102+
if err != nil {
103+
// Handle error
104+
}
105+
switch mspid {
106+
case "org1MSP":
107+
err = id.AssertAttributeValue("attr1", "true")
108+
case "org2MSP":
109+
err = id.AssertAttributeValue("attr2", "true")
110+
default:
111+
err = errors.New("Wrong MSP")
112+
}
113+
```
114+
Although it is not required, it is more efficient to make the `cid.New` call
115+
to get the ClientIdentity object if you need to perform multiple operations,
116+
as demonstrated above.
117+
118+
## Adding Attributes to Identities
119+
120+
This section describes how to add custom attributes to certificates when
121+
using Hyperledger Fabric CA as well as when using an external CA.
122+
123+
#### Managing attributes with Fabric CA
124+
125+
There are two methods of adding attributes to an enrollment certificate
126+
with fabric-ca:
127+
128+
1. When you register an identity, you can specify that an enrollment certificate
129+
issued for the identity should by default contain an attribute. This behavior
130+
can be overridden at enrollment time, but this is useful for establishing
131+
default behavior and, assuming registration occurs outside of your application,
132+
does not require any application change.
133+
134+
The following shows how to register *user1* with two attributes:
135+
*app1Admin* and *email*.
136+
The ":ecert" suffix causes the *appAdmin* attribute to be inserted into user1's
137+
enrollment certificate by default. The *email* attribute is not added
138+
to the enrollment certificate by default.
139+
140+
```
141+
fabric-ca-client register --id.name user1 --id.secret user1pw --id.type user --id.affiliation org1 --id.attrs 'app1Admin=true:ecert,email=user1@gmail.com'
142+
```
143+
144+
2. When you enroll an identity, you may request that one or more attributes
145+
be added to the certificate.
146+
For each attribute requested, you may specify whether the attribute is
147+
optional or not. If it is not optional but does not exist for the identity,
148+
enrollment fails.
149+
150+
The following shows how to enroll *user1* with the *email* attribute,
151+
without the *app1Admin* attribute and optionally with the *phone* attribute
152+
(if the user possesses *phone* attribute).
153+
```
154+
fabric-ca-client enroll -u http://user1:user1pw@localhost:7054 --enrollment.attrs "email,phone:opt"
155+
```
156+
#### Attribute format in a certificate
157+
158+
Attributes are stored inside an X509 certificate as an extension with an
159+
ASN.1 OID (Abstract Syntax Notation Object IDentifier)
160+
of `1.2.3.4.5.6.7.8.1`. The value of the extension is a JSON string of the
161+
form `{"attrs":{<attrName>:<attrValue}}`. The following is a sample of a
162+
certificate which contains the `attr1` attribute with a value of `val1`.
163+
See the final entry in the *X509v3 extensions* section. Note also that the JSON
164+
entry could contain multiple attributes, though this sample shows only one.
165+
166+
```
167+
Certificate:
168+
Data:
169+
Version: 3 (0x2)
170+
Serial Number:
171+
1e:49:98:e9:f4:4f:d0:03:53:bf:36:81:c0:a0:a4:31:96:4f:52:75
172+
Signature Algorithm: ecdsa-with-SHA256
173+
Issuer: CN=fabric-ca-server
174+
Validity
175+
Not Before: Sep 8 03:42:00 2017 GMT
176+
Not After : Sep 8 03:42:00 2018 GMT
177+
Subject: CN=MyTestUserWithAttrs
178+
Subject Public Key Info:
179+
Public Key Algorithm: id-ecPublicKey
180+
EC Public Key:
181+
pub:
182+
04:e6:07:5a:f7:09:d5:af:38:e3:f7:a2:90:77:0e:
183+
32:67:5b:70:a7:37:ca:b5:c9:d8:91:77:39:ae:03:
184+
a0:36:ad:72:b3:3c:89:6d:1e:f6:1b:6d:2a:88:49:
185+
92:6e:6e:cc:bc:81:52:fa:19:88:18:5c:d7:6e:eb:
186+
d4:73:cc:51:79
187+
ASN1 OID: prime256v1
188+
X509v3 extensions:
189+
X509v3 Key Usage: critical
190+
Certificate Sign
191+
X509v3 Basic Constraints: critical
192+
CA:FALSE
193+
X509v3 Subject Key Identifier:
194+
D8:28:B4:C0:BC:92:4A:D3:C3:8C:54:6C:08:86:33:10:A6:8D:83:AE
195+
X509v3 Authority Key Identifier:
196+
keyid:C4:B3:FE:76:0D:E2:DE:3C:FC:75:FB:AE:55:86:04:F0:BB:7F:F6:01
197+
198+
X509v3 Subject Alternative Name:
199+
DNS:Anils-MacBook-Pro.local
200+
1.2.3.4.5.6.7.8.1:
201+
{"attrs":{"attr1":"val1"}}
202+
Signature Algorithm: ecdsa-with-SHA256
203+
30:45:02:21:00:fb:84:a9:65:29:b2:f4:d3:bc:1a:8b:47:92:
204+
5e:41:27:2d:26:ec:f7:cd:aa:86:46:a4:ac:da:25:be:40:1d:
205+
c5:02:20:08:3f:49:86:58:a7:20:48:64:4c:30:1b:da:a9:a2:
206+
f2:b4:16:28:f6:fd:e1:46:dd:6b:f2:3f:2f:37:4a:4c:72
207+
```
208+
209+
If you want to use the client identity library to extract or assert attribute
210+
values as described previously but you are not using Hyperledger Fabric CA,
211+
then you must ensure that the certificates which are issued by your external CA
212+
contain attributes of the form shown above. In particular, the certificates
213+
must contain the `1.2.3.4.5.6.7.8.1` X509v3 extension with a JSON value
214+
containing the attribute names and values for the identity.

‎core/chaincode/lib/cid/cid.go

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cid
18+
19+
import (
20+
"crypto/x509"
21+
"encoding/asn1"
22+
"encoding/hex"
23+
"encoding/pem"
24+
"fmt"
25+
26+
"github.com/golang/protobuf/proto"
27+
"github.com/hyperledger/fabric/common/attrmgr"
28+
"github.com/hyperledger/fabric/protos/msp"
29+
"github.com/pkg/errors"
30+
)
31+
32+
// GetID returns the ID associated with the invoking identity. This ID
33+
// is guaranteed to be unique within the MSP.
34+
func GetID(stub ChaincodeStubInterface) (string, error) {
35+
c, err := New(stub)
36+
if err != nil {
37+
return "", err
38+
}
39+
return c.GetID()
40+
}
41+
42+
// GetMSPID returns the ID of the MSP associated with the identity that
43+
// submitted the transaction
44+
func GetMSPID(stub ChaincodeStubInterface) (string, error) {
45+
c, err := New(stub)
46+
if err != nil {
47+
return "", err
48+
}
49+
return c.GetMSPID()
50+
}
51+
52+
// GetAttributeValue returns value of the specified attribute
53+
func GetAttributeValue(stub ChaincodeStubInterface, attrName string) (value string, found bool, err error) {
54+
c, err := New(stub)
55+
if err != nil {
56+
return "", false, err
57+
}
58+
return c.GetAttributeValue(attrName)
59+
}
60+
61+
// AssertAttributeValue checks to see if an attribute value equals the specified value
62+
func AssertAttributeValue(stub ChaincodeStubInterface, attrName, attrValue string) error {
63+
c, err := New(stub)
64+
if err != nil {
65+
return err
66+
}
67+
return c.AssertAttributeValue(attrName, attrValue)
68+
}
69+
70+
// GetX509Certificate returns the X509 certificate associated with the client,
71+
// or nil if it was not identified by an X509 certificate.
72+
func GetX509Certificate(stub ChaincodeStubInterface) (*x509.Certificate, error) {
73+
c, err := New(stub)
74+
if err != nil {
75+
return nil, err
76+
}
77+
return c.GetX509Certificate()
78+
}
79+
80+
// ClientIdentityImpl implements the ClientIdentity interface
81+
type clientIdentityImpl struct {
82+
stub ChaincodeStubInterface
83+
mspID string
84+
cert *x509.Certificate
85+
attrs *attrmgr.Attributes
86+
}
87+
88+
// New returns an instance of ClientIdentity
89+
func New(stub ChaincodeStubInterface) (ClientIdentity, error) {
90+
c := &clientIdentityImpl{stub: stub}
91+
err := c.init()
92+
if err != nil {
93+
return nil, err
94+
}
95+
return c, nil
96+
}
97+
98+
// GetID returns the ID associated with the invoking identity. This ID
99+
// is guaranteed to be unique within the MSP.
100+
func (c *clientIdentityImpl) GetID() (string, error) {
101+
return getDN(c.cert), nil
102+
}
103+
104+
// GetMSPID returns the ID of the MSP associated with the identity that
105+
// submitted the transaction
106+
func (c *clientIdentityImpl) GetMSPID() (string, error) {
107+
return c.mspID, nil
108+
}
109+
110+
// GetAttributeValue returns value of the specified attribute
111+
func (c *clientIdentityImpl) GetAttributeValue(attrName string) (value string, found bool, err error) {
112+
if c.attrs == nil {
113+
return "", false, nil
114+
}
115+
return c.attrs.Value(attrName)
116+
}
117+
118+
// AssertAttributeValue checks to see if an attribute value equals the specified value
119+
func (c *clientIdentityImpl) AssertAttributeValue(attrName, attrValue string) error {
120+
val, ok, err := c.GetAttributeValue(attrName)
121+
if err != nil {
122+
return err
123+
}
124+
if !ok {
125+
return errors.Errorf("Attribute '%s' was not found", attrName)
126+
}
127+
if val != attrValue {
128+
return errors.Errorf("Attribute '%s' equals '%s', not '%s'", attrName, val, attrValue)
129+
}
130+
return nil
131+
}
132+
133+
// GetX509Certificate returns the X509 certificate associated with the client,
134+
// or nil if it was not identified by an X509 certificate.
135+
func (c *clientIdentityImpl) GetX509Certificate() (*x509.Certificate, error) {
136+
return c.cert, nil
137+
}
138+
139+
// Initialize the client
140+
func (c *clientIdentityImpl) init() error {
141+
signingID, err := c.getIdentity()
142+
if err != nil {
143+
return err
144+
}
145+
c.mspID = signingID.GetMspid()
146+
idbytes := signingID.GetIdBytes()
147+
block, _ := pem.Decode(idbytes)
148+
if block == nil || block.Type != "CERTIFICATE" {
149+
bt := "none"
150+
if block != nil {
151+
bt = block.Type
152+
}
153+
return errors.Errorf("unexpected PEM block found. Expected a certificate but found a block of type: %s", bt)
154+
}
155+
cert, err := x509.ParseCertificate(block.Bytes)
156+
if err != nil {
157+
return errors.Wrap(err, "failed to parse certificate")
158+
}
159+
c.cert = cert
160+
attrs, err := attrmgr.New().GetAttributesFromCert(cert)
161+
if err != nil {
162+
return errors.WithMessage(err, "failed to get attributes from the transaction invoker's certificate")
163+
}
164+
c.attrs = attrs
165+
return nil
166+
}
167+
168+
// Unmarshals the bytes returned by ChaincodeStubInterface.GetCreator method and
169+
// returns the resulting msp.SerializedIdentity object
170+
func (c *clientIdentityImpl) getIdentity() (*msp.SerializedIdentity, error) {
171+
sid := &msp.SerializedIdentity{}
172+
creator, err := c.stub.GetCreator()
173+
if err != nil || creator == nil {
174+
return nil, errors.WithMessage(err, "failed to get transaction invoker's identity from the chaincode stub")
175+
}
176+
err = proto.Unmarshal(creator, sid)
177+
if err != nil {
178+
return nil, errors.Wrap(err, "failed to unmarshal transaction invoker's identity")
179+
}
180+
return sid, nil
181+
}
182+
183+
// Get the DN (distinquished name) associated with the subject of the certificate.
184+
// NOTE: This code is almost a direct copy of the String() function in
185+
// https://go-review.googlesource.com/c/go/+/67270/1/src/crypto/x509/pkix/pkix.go#26
186+
// which returns a DN as defined by RFC 2253.
187+
func getDN(cert *x509.Certificate) string {
188+
r := cert.Subject.ToRDNSequence()
189+
s := ""
190+
for i := 0; i < len(r); i++ {
191+
rdn := r[len(r)-1-i]
192+
if i > 0 {
193+
s += ","
194+
}
195+
for j, tv := range rdn {
196+
if j > 0 {
197+
s += "+"
198+
}
199+
typeString := tv.Type.String()
200+
typeName, ok := attributeTypeNames[typeString]
201+
if !ok {
202+
derBytes, err := asn1.Marshal(tv.Value)
203+
if err == nil {
204+
s += typeString + "=#" + hex.EncodeToString(derBytes)
205+
continue // No value escaping necessary.
206+
}
207+
typeName = typeString
208+
}
209+
valueString := fmt.Sprint(tv.Value)
210+
escaped := ""
211+
begin := 0
212+
for idx, c := range valueString {
213+
if (idx == 0 && (c == ' ' || c == '#')) ||
214+
(idx == len(valueString)-1 && c == ' ') {
215+
escaped += valueString[begin:idx]
216+
escaped += "\\" + string(c)
217+
begin = idx + 1
218+
continue
219+
}
220+
switch c {
221+
case ',', '+', '"', '\\', '<', '>', ';':
222+
escaped += valueString[begin:idx]
223+
escaped += "\\" + string(c)
224+
begin = idx + 1
225+
}
226+
}
227+
escaped += valueString[begin:]
228+
s += typeName + "=" + escaped
229+
}
230+
}
231+
return s
232+
}
233+
234+
var attributeTypeNames = map[string]string{
235+
"2.5.4.6": "C",
236+
"2.5.4.10": "O",
237+
"2.5.4.11": "OU",
238+
"2.5.4.3": "CN",
239+
"2.5.4.5": "SERIALNUMBER",
240+
"2.5.4.7": "L",
241+
"2.5.4.8": "ST",
242+
"2.5.4.9": "STREET",
243+
"2.5.4.17": "POSTALCODE",
244+
}

‎core/chaincode/lib/cid/cid_test.go

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cid_test
17+
18+
import (
19+
"testing"
20+
21+
"github.com/golang/protobuf/proto"
22+
"github.com/hyperledger/fabric/core/chaincode/lib/cid"
23+
"github.com/hyperledger/fabric/protos/msp"
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
const certWithOutAttrs = `-----BEGIN CERTIFICATE-----
28+
MIICXTCCAgSgAwIBAgIUeLy6uQnq8wwyElU/jCKRYz3tJiQwCgYIKoZIzj0EAwIw
29+
eTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
30+
biBGcmFuY2lzY28xGTAXBgNVBAoTEEludGVybmV0IFdpZGdldHMxDDAKBgNVBAsT
31+
A1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTcwOTA4MDAxNTAwWhcNMTgw
32+
OTA4MDAxNTAwWjBdMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xp
33+
bmExFDASBgNVBAoTC0h5cGVybGVkZ2VyMQ8wDQYDVQQLEwZGYWJyaWMxDjAMBgNV
34+
BAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFq/90YMuH4tWugHa
35+
oyZtt4Mbwgv6CkBSDfYulVO1CVInw1i/k16DocQ/KSDTeTfgJxrX1Ree1tjpaodG
36+
1wWyM6OBhTCBgjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
37+
FgQUhKs/VJ9IWJd+wer6sgsgtZmxZNwwHwYDVR0jBBgwFoAUIUd4i/sLTwYWvpVr
38+
TApzcT8zv/kwIgYDVR0RBBswGYIXQW5pbHMtTWFjQm9vay1Qcm8ubG9jYWwwCgYI
39+
KoZIzj0EAwIDRwAwRAIgCoXaCdU8ZiRKkai0QiXJM/GL5fysLnmG2oZ6XOIdwtsC
40+
IEmCsI8Mhrvx1doTbEOm7kmIrhQwUVDBNXCWX1t3kJVN
41+
-----END CERTIFICATE-----
42+
`
43+
const certWithAttrs = `-----BEGIN CERTIFICATE-----
44+
MIIB6TCCAY+gAwIBAgIUHkmY6fRP0ANTvzaBwKCkMZZPUnUwCgYIKoZIzj0EAwIw
45+
GzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0xNzA5MDgwMzQyMDBaFw0x
46+
ODA5MDgwMzQyMDBaMB4xHDAaBgNVBAMTE015VGVzdFVzZXJXaXRoQXR0cnMwWTAT
47+
BgcqhkjOPQIBBggqhkjOPQMBBwNCAATmB1r3CdWvOOP3opB3DjJnW3CnN8q1ydiR
48+
dzmuA6A2rXKzPIltHvYbbSqISZJubsy8gVL6GYgYXNdu69RzzFF5o4GtMIGqMA4G
49+
A1UdDwEB/wQEAwICBDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTYKLTAvJJK08OM
50+
VGwIhjMQpo2DrjAfBgNVHSMEGDAWgBTEs/52DeLePPx1+65VhgTwu3/2ATAiBgNV
51+
HREEGzAZghdBbmlscy1NYWNCb29rLVByby5sb2NhbDAmBggqAwQFBgcIAQQaeyJh
52+
dHRycyI6eyJhdHRyMSI6InZhbDEifX0wCgYIKoZIzj0EAwIDSAAwRQIhAPuEqWUp
53+
svTTvBqLR5JeQSctJuz3zaqGRqSs2iW+QB3FAiAIP0mGWKcgSGRMMBvaqaLytBYo
54+
9v3hRt1r8j8vN0pMcg==
55+
-----END CERTIFICATE-----
56+
`
57+
58+
func TestClient(t *testing.T) {
59+
stub, err := getMockStub()
60+
assert.NoError(t, err, "Failed to get mock submitter")
61+
sinfo, err := cid.New(stub)
62+
assert.NoError(t, err, "Error getting submitter of the transaction")
63+
id, err := cid.GetID(stub)
64+
assert.NoError(t, err, "Error getting ID of the submitter of the transaction")
65+
assert.NotEmpty(t, id, "Transaction submitter ID should not be empty")
66+
t.Logf("The client's ID is: %s", id)
67+
cert, err := cid.GetX509Certificate(stub)
68+
assert.NoError(t, err, "Error getting X509 certificate of the submitter of the transaction")
69+
assert.NotNil(t, cert, "Transaction submitter certificate should not be nil")
70+
mspid, err := cid.GetMSPID(stub)
71+
assert.NoError(t, err, "Error getting MSP ID of the submitter of the transaction")
72+
assert.NotEmpty(t, mspid, "Transaction submitter MSP ID should not be empty")
73+
_, found, err := sinfo.GetAttributeValue("foo")
74+
assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction")
75+
assert.False(t, found, "Attribute 'foo' should not be found in the submitter cert")
76+
err = cid.AssertAttributeValue(stub, "foo", "")
77+
assert.Error(t, err, "AssertAttributeValue should have returned an error with no attribute")
78+
79+
stub, err = getMockStubWithAttrs()
80+
assert.NoError(t, err, "Failed to get mock submitter")
81+
sinfo, err = cid.New(stub)
82+
assert.NoError(t, err, "Failed to new client")
83+
attrVal, found, err := sinfo.GetAttributeValue("attr1")
84+
assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction")
85+
assert.True(t, found, "Attribute 'attr1' should be found in the submitter cert")
86+
assert.Equal(t, attrVal, "val1", "Value of attribute 'attr1' should be 'val1'")
87+
attrVal, found, err = cid.GetAttributeValue(stub, "attr1")
88+
assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction")
89+
assert.True(t, found, "Attribute 'attr1' should be found in the submitter cert")
90+
assert.Equal(t, attrVal, "val1", "Value of attribute 'attr1' should be 'val1'")
91+
err = cid.AssertAttributeValue(stub, "attr1", "val1")
92+
assert.NoError(t, err, "Error in AssertAttributeValue")
93+
err = cid.AssertAttributeValue(stub, "attr1", "val2")
94+
assert.Error(t, err, "Assert should have failed; value was val1, not val2")
95+
96+
// Error case1
97+
stub, err = getMockStubWithNilCreator()
98+
assert.NoError(t, err, "Failed to get mock submitter")
99+
sinfo, err = cid.New(stub)
100+
assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with nil creator is passed")
101+
102+
// Error case2
103+
stub, err = getMockStubWithFakeCreator()
104+
assert.NoError(t, err, "Failed to get mock submitter")
105+
sinfo, err = cid.New(stub)
106+
assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with fake creator is passed")
107+
}
108+
109+
func getMockStub() (cid.ChaincodeStubInterface, error) {
110+
stub := &mockStub{}
111+
sid := &msp.SerializedIdentity{Mspid: "DEFAULT",
112+
IdBytes: []byte(certWithOutAttrs)}
113+
b, err := proto.Marshal(sid)
114+
if err != nil {
115+
return nil, err
116+
}
117+
stub.creator = b
118+
return stub, nil
119+
}
120+
121+
func getMockStubWithAttrs() (cid.ChaincodeStubInterface, error) {
122+
stub := &mockStub{}
123+
sid := &msp.SerializedIdentity{Mspid: "DEFAULT",
124+
IdBytes: []byte(certWithAttrs)}
125+
b, err := proto.Marshal(sid)
126+
if err != nil {
127+
return nil, err
128+
}
129+
stub.creator = b
130+
return stub, nil
131+
}
132+
133+
func getMockStubWithNilCreator() (cid.ChaincodeStubInterface, error) {
134+
c := &mockStub{}
135+
c.creator = nil
136+
return c, nil
137+
}
138+
139+
func getMockStubWithFakeCreator() (cid.ChaincodeStubInterface, error) {
140+
c := &mockStub{}
141+
c.creator = []byte("foo")
142+
return c, nil
143+
}
144+
145+
type mockStub struct {
146+
creator []byte
147+
}
148+
149+
func (s *mockStub) GetCreator() ([]byte, error) {
150+
return s.creator, nil
151+
}

‎core/chaincode/lib/cid/interfaces.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cid
18+
19+
import "crypto/x509"
20+
21+
// ChaincodeStubInterface is used by deployable chaincode apps to get identity
22+
// of the agent (or user) submitting the transaction.
23+
type ChaincodeStubInterface interface {
24+
// GetCreator returns `SignatureHeader.Creator` (e.g. an identity)
25+
// of the `SignedProposal`. This is the identity of the agent (or user)
26+
// submitting the transaction.
27+
GetCreator() ([]byte, error)
28+
}
29+
30+
// ClientIdentity represents information about the identity that submitted the
31+
// transaction
32+
type ClientIdentity interface {
33+
34+
// GetID returns the ID associated with the invoking identity. This ID
35+
// is guaranteed to be unique within the MSP.
36+
GetID() (string, error)
37+
38+
// Return the MSP ID of the client
39+
GetMSPID() (string, error)
40+
41+
// GetAttributeValue returns the value of the client's attribute named `attrName`.
42+
// If the client possesses the attribute, `found` is true and `value` equals the
43+
// value of the attribute.
44+
// If the client does not possess the attribute, `found` is false and `value`
45+
// equals "".
46+
GetAttributeValue(attrName string) (value string, found bool, err error)
47+
48+
// AssertAttributeValue verifies that the client has the attribute named `attrName`
49+
// with a value of `attrValue`; otherwise, an error is returned.
50+
AssertAttributeValue(attrName, attrValue string) error
51+
52+
// GetX509Certificate returns the X509 certificate associated with the client,
53+
// or nil if it was not identified by an X509 certificate.
54+
GetX509Certificate() (*x509.Certificate, error)
55+
}

‎msp/identities.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (id *identity) Verify(msg []byte, sig []byte) error {
178178
func (id *identity) Serialize() ([]byte, error) {
179179
// mspIdentityLogger.Infof("Serializing identity %s", id.id)
180180

181-
pb := &pem.Block{Bytes: id.cert.Raw}
181+
pb := &pem.Block{Bytes: id.cert.Raw, Type: "CERTIFICATE"}
182182
pemBytes := pem.EncodeToMemory(pb)
183183
if pemBytes == nil {
184184
return nil, fmt.Errorf("Encoding of identitiy failed")

0 commit comments

Comments
 (0)
Please sign in to comment.