|
| 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. |
0 commit comments