This post will show you how to implement a basic Hyperledger Fabric ACL using the Fabric package CID.
ACL stands for Access Control List and it is use to control the accessibility of resources for users. An example of ACL is that only a user who is registered with the library may borrow a book.
Hyperledger Fabric is a blockchain framework for creating permissioned blockchains. This post will show you how to use the Hyperledger Fabric CID package to limit access to resources.
The ACL implementation requires us to work on 2 components: the API server, and the Chaincode. We'll be writing both the API server and the Chaincode in Go(Lang). The Go version we'll be using is version 1.12.1, so make sure you have it installed.
In this post we'll be using a library as the example. In a library, only a person who is registered with the library can borrow books. In our Fabric application we'll make a chaincode function to borrow a book, and when called, the chaincode will check first if the user is a registered member of the library. A check is needed to be done so that a user who is not registered won't be able to borrow books.
In our chaincode, the first thing to do is to import the Go Hyperledger Fabric CID module. Below is the url for the module.
0 1 2
import ( "github.com/hyperledger/fabric/core/chaincode/lib/cid" )
When the Borrow function is called, we'll do a check with the user before we'll call the chaincode function. In Hyperledger Fabric, ACL is done by declaring some attributes of the user when creating the user. These attributes can be check and assert inside the chaincode using the CID module. The code below shows how to use the module to assert that the chaincode invoking user has the attribute of registered
set to true
. If the user doesn't have the attribute set to true
then an error is returned, otherwise the chaincode function is called and it's response is returned.
0 1 2 3 4 5 6 7 8 9 10 11 12 13
// Assert that the chaincode invoker have the attribute of 'registered' set to 'true'. // Returns an error, if attribute 'registered' is not set to 'true'. err := cid.AssertAttributeValue(stub, "registered", "true") // Check if an error was returned (not nil). if err != nil { // Return error. return Reponse{}, fmt.Errorf("User not registered") } // Otherwise, if there's no error ( when 'registered' is set to 'true'), then calls the chaincode // function Borrow and return it's response. res, err := c.Borrow(stub, args) return res, err
As stated at the beginning of the post, the API server will also be written in Go. To implement an API server (written in Go) that communicates with the Hyperledger Fabric blockchain network, we'll require Hyperledger Fabric Go SDK. Hyperledger Fabric provides their SDK in many languages (NodeJS, Java, Go, Python). Currently at Hyperledger Fabric version 1.4, the only officially supported SDK are for NodeJS and Java.
The modules that are required for creating the user are:
0 1 2 3 4
import ( "github.com/hyperledger/fabric-sdk-go/pkg/core/config" mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" )
Below is the code for creating the user with the attribute. The steps below describe what is being done in the code.
config
module from the fabric-sdk-go
is use to import the configuration from the file.registered
attribute with a value of true
. This is the attribute that will be asserted in the chaincode when calling the Borrow
function.enrolled
state, where it is able to invoke chaincode function etc. A registered user can also be of the state revoked
. This revoke
state, prevent the user from taking certain actions within the Fabric network. This first level of ACL is taken care of by the Certificate Authority (CA).0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Create a Fabric SDK instance from YAML config file. sdk, err := fabsdk.New(config.FromFile(ConfigFile)) if err != nil { return errors.WithMessage(err, "failed to create SDK") } // The username of the user we're creating. username := "user0000" // Create an Membership Service Provider Client from the Fabric SDK context. mspClient, err := mspclient.New(sdk.Context()) if err != nil { return errors.Cause(err) } // Register the user along with it's attribute. enrollmentSecret, err := mspClient.Register(&mspclient.RegistrationRequest{ Name: username, Type: "user", MaxEnrollments: 0, Affiliation: "org1", CAName: CaID, Attributes: []mspclient.Attribute{{Name: "registered", Value: "true"}}, }) if err != nil { return errors.Cause(err) } // Enroll recently registered user. err = mspClient.Enroll(username, mspclient.WithSecret(enrollmentSecret)) if err != nil { return errors.Cause(err) }
After a user is created it can be use to invoke a chaincode function. The code below shows how to creating a signing identity to use to for chaincode function invocation.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// The registered and enrolled username. username := "user0" // Creating a signing identity with the user. signingIdentity, err := mspClient.GetSigningIdentity(username) if err != nil { return channel.Response{}, err } // Create a Fabric channel context. clientChannelContext := sdk.ChannelContext( ChannelID, fabsdk.WithUser(username), fabsdk.WithOrg(OrgName), fabsdk.WithIdentity(signingIdentity), ) // Create a channel client. client, err := channel.New(clientChannelContext) if err != nil { return channel.Response{}, err } //Prepare and execute the chaincode transaction. resp, err := client.Execute(channel.Request{ ChaincodeID: ChainCodeID, Fcn: "Borrow", Args: [][]byte{req}, }, channel.WithRetry(retry.DefaultResMgmtOpts)) if err != nil { return resp, err } return resp, nil
As you can see, there's not that much code involved in implementing a Chaincode level ACL with CID. The Hyperledger Fabric SDK provides you with most things to be able to easily get what you want done. I hope you've learn something new here today from this post.