github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/examples/chaincode/go/rbac_tcerts_no_attrs/rbac.go (about) 1 /* 2 Copyright IBM Corp 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 main 18 19 import ( 20 "errors" 21 "fmt" 22 23 "encoding/asn1" 24 "encoding/base64" 25 "strings" 26 27 "github.com/hyperledger/fabric/accesscontrol/impl" 28 "github.com/hyperledger/fabric/core/chaincode/shim" 29 pb "github.com/hyperledger/fabric/protos/peer" 30 "github.com/op/go-logging" 31 ) 32 33 var myLogger = logging.MustGetLogger("rbac_chaincode") 34 35 // RBACMetadata metadata structure for RBAC information 36 type RBACMetadata struct { 37 Cert []byte 38 Sigma []byte 39 } 40 41 // RBACChaincode RBAC chaincode structure 42 type RBACChaincode struct { 43 } 44 45 // Init method will be called during deployment 46 func (t *RBACChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 47 myLogger.Info("Init") 48 49 myLogger.Debug("Creating RBAC Table...") 50 51 // Create RBAC table 52 err := stub.CreateTable("RBAC", []*shim.ColumnDefinition{ 53 &shim.ColumnDefinition{Name: "ID", Type: shim.ColumnDefinition_BYTES, Key: true}, 54 &shim.ColumnDefinition{Name: "Roles", Type: shim.ColumnDefinition_STRING, Key: false}, 55 }) 56 if err != nil { 57 return shim.Error("Failed creating RBAC table.") 58 } 59 60 myLogger.Debug("Assign 'admin' role...") 61 62 // Give to the deployer the role 'admin' 63 deployer, err := stub.GetCallerMetadata() 64 if err != nil { 65 return shim.Error("Failed getting metadata.") 66 } 67 if len(deployer) == 0 { 68 return shim.Error("Invalid admin certificate. Empty.") 69 } 70 71 myLogger.Debug("Add admin [% x][%s]", deployer, "admin") 72 ok, err := stub.InsertRow("RBAC", shim.Row{ 73 Columns: []*shim.Column{ 74 &shim.Column{Value: &shim.Column_Bytes{Bytes: deployer}}, 75 &shim.Column{Value: &shim.Column_String_{String_: "admin"}}, 76 }, 77 }) 78 if !ok && err == nil { 79 return shim.Error("Failed initiliazing RBAC entries.") 80 } 81 if err != nil { 82 return shim.Error(fmt.Sprintf("Failed initiliazing RBAC entries [%s]", err)) 83 } 84 85 myLogger.Debug("Done.") 86 87 return shim.Success(nil) 88 } 89 90 // Invoke Run callback representing the invocation of a chaincode 91 func (t *RBACChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 92 function, args := stub.GetFunctionAndParameters() 93 // Handle different functions 94 switch function { 95 case "addRole": 96 return t.addRole(stub, args) 97 case "write": 98 return t.write(stub, args) 99 case "read": 100 return t.read(stub, args) 101 } 102 103 return shim.Error(fmt.Sprintf("Received unknown function invocation [%s]", function)) 104 } 105 106 func (t *RBACChaincode) addRole(stub shim.ChaincodeStubInterface, args []string) pb.Response { 107 if len(args) != 2 { 108 return shim.Error("Incorrect number of arguments. Expecting 2") 109 } 110 id, err := base64.StdEncoding.DecodeString(args[0]) 111 if err != nil { 112 return shim.Error(fmt.Sprintf("Failed decoding tcert: %s", err)) 113 } 114 //id := []byte(args[0]) 115 role := args[1] 116 117 myLogger.Debug("Add role [% x][%s]", id, role) 118 119 // Verify that the invoker has the 'admin' role 120 ok, _, err := t.hasInvokerRole(stub, "admin") 121 if err != nil { 122 return shim.Error(fmt.Sprintf("Failed checking role [%s]", err)) 123 } 124 if !ok { 125 return shim.Error("The invoker does not have the required roles.") 126 } 127 128 // Add role to id 129 myLogger.Debug("Permission granted to the invoker") 130 131 // Retrieve id's row 132 var columns []shim.Column 133 idCol := shim.Column{Value: &shim.Column_Bytes{Bytes: id}} 134 columns = append(columns, idCol) 135 row, err := stub.GetRow("RBAC", columns) 136 if err != nil { 137 return shim.Error(fmt.Sprintf("Failed retriving associated row [%s]", err)) 138 } 139 if len(row.Columns) == 0 { 140 // Insert row 141 ok, err = stub.InsertRow("RBAC", shim.Row{ 142 Columns: []*shim.Column{ 143 &shim.Column{Value: &shim.Column_Bytes{Bytes: id}}, 144 &shim.Column{Value: &shim.Column_String_{String_: role}}, 145 }, 146 }) 147 if err != nil { 148 return shim.Error(fmt.Sprintf("Failed inserting row [%s]", err)) 149 } 150 if !ok { 151 return shim.Error("Failed inserting row.") 152 } 153 154 } else { 155 // Update row 156 ok, err = stub.ReplaceRow("RBAC", shim.Row{ 157 Columns: []*shim.Column{ 158 &shim.Column{Value: &shim.Column_Bytes{Bytes: id}}, 159 &shim.Column{Value: &shim.Column_String_{String_: row.Columns[1].GetString_() + " " + role}}, 160 }, 161 }) 162 if err != nil { 163 return shim.Error(fmt.Sprintf("Failed replacing row [%s]", err)) 164 } 165 if !ok { 166 return shim.Error("Failed replacing row.") 167 } 168 } 169 170 return shim.Success(nil) 171 } 172 173 func (t *RBACChaincode) read(stub shim.ChaincodeStubInterface, args []string) pb.Response { 174 if len(args) != 0 { 175 return shim.Error("Incorrect number of arguments. Expecting 0") 176 } 177 myLogger.Debug("Read...") 178 179 // Verify that the invoker has the 'reader' role 180 ok, _, err := t.hasInvokerRole(stub, "reader") 181 if err != nil { 182 return shim.Error(fmt.Sprintf("Failed checking role [%s]", err)) 183 } 184 if !ok { 185 return shim.Error("The invoker does not have the required roles") 186 } 187 188 res, err := stub.GetState("state") 189 if err != nil { 190 return shim.Error(fmt.Sprintf("Failed getting state [%s]", err)) 191 } 192 193 myLogger.Debug("State [%s]", string(res)) 194 195 return shim.Success(res) 196 } 197 198 func (t *RBACChaincode) write(stub shim.ChaincodeStubInterface, args []string) pb.Response { 199 if len(args) != 1 { 200 return shim.Error("Incorrect number of arguments. Expecting 1") 201 } 202 203 value := args[0] 204 205 myLogger.Debug("Write [%s]", value) 206 207 // Verify that the invoker has the 'writer' role 208 ok, _, err := t.hasInvokerRole(stub, "writer") 209 if err != nil { 210 return shim.Error(fmt.Sprintf("Failed checking role [%s]", err)) 211 } 212 if !ok { 213 return shim.Error("The invoker does not have the required roles.") 214 } 215 216 err = stub.PutState("state", []byte(value)) 217 if err != nil { 218 return shim.Error(err.Error()) 219 } 220 221 return shim.Success(nil) 222 } 223 224 func (t *RBACChaincode) hasInvokerRole(stub shim.ChaincodeStubInterface, role string) (bool, []byte, error) { 225 // In order to enforce access control, we require that the 226 // metadata contains the following items: 227 // 1. a certificate Cert 228 // 2. a signature Sigma under the signing key corresponding 229 // to the verification key inside Cert of : 230 // (a) Cert; 231 // (b) The payload of the transaction (namely, function name and args) and 232 // (c) the transaction binding. 233 234 // Verify Sigma=Sign(certificate.sk, Cert||tx.Payload||tx.Binding) against Cert.vk 235 236 // Unmarshall metadata 237 metadata, err := stub.GetCallerMetadata() 238 rbacMetadata := new(RBACMetadata) 239 _, err = asn1.Unmarshal(metadata, rbacMetadata) 240 if err != nil { 241 return false, nil, fmt.Errorf("Failed unmarshalling metadata [%s]", err) 242 } 243 244 // Verify signature 245 payload, err := stub.GetArgsSlice() 246 if err != nil { 247 return false, nil, errors.New("Failed getting payload") 248 } 249 binding, err := stub.GetBinding() 250 if err != nil { 251 return false, nil, errors.New("Failed getting binding") 252 } 253 254 myLogger.Debug("passed certificate [% x]", rbacMetadata.Cert) 255 myLogger.Debug("passed sigma [% x]", rbacMetadata.Sigma) 256 myLogger.Debug("passed payload [% x]", payload) 257 myLogger.Debug("passed binding [% x]", binding) 258 259 ok, err := impl.NewAccessControlShim(stub).VerifySignature( 260 rbacMetadata.Cert, 261 rbacMetadata.Sigma, 262 append(rbacMetadata.Cert, append(payload, binding...)...), 263 ) 264 if err != nil { 265 return false, nil, fmt.Errorf("Failed verifying signature [%s]", err) 266 } 267 if !ok { 268 return false, nil, fmt.Errorf("Signature is not valid!") 269 } 270 271 myLogger.Debug("Signature verified. Check for role...") 272 273 myLogger.Debug("ID [% x]", rbacMetadata.Cert) 274 275 // Check role 276 var columns []shim.Column 277 idCol := shim.Column{Value: &shim.Column_Bytes{Bytes: rbacMetadata.Cert}} 278 columns = append(columns, idCol) 279 row, err := stub.GetRow("RBAC", columns) 280 if err != nil { 281 return false, nil, fmt.Errorf("Failed retrieveing RBAC row [%s]", err) 282 } 283 if len(row.GetColumns()) == 0 { 284 return false, nil, fmt.Errorf("Failed retrieveing RBAC row [%s]", err) 285 } 286 287 myLogger.Debug("#Columns: [%d]", len(row.Columns)) 288 roles := row.Columns[1].GetString_() 289 290 result := strings.Split(roles, " ") 291 for i := range result { 292 if result[i] == role { 293 myLogger.Debug("Role found.") 294 return true, rbacMetadata.Cert, nil 295 } 296 } 297 298 myLogger.Debug("Role not found.") 299 300 return false, nil, nil 301 } 302 303 func main() { 304 err := shim.Start(new(RBACChaincode)) 305 if err != nil { 306 fmt.Printf("Error starting AssetManagementChaincode: %s", err) 307 } 308 }