Implementing Fabric ACL with CID

05-03-2019




Imgur



This post will show you how to implement a basic Hyperledger Fabric ACL using the Fabric package CID.



ACL

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

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.



Development

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.


Chaincode

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

API Server

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.

  1. First we create a Fabric SDK instance from a Fabric config file. The config file is a YAML file that declare the Fabric network configuration such as the name and host of the Peers, CA, Orderer etc. The config module from the fabric-sdk-go is use to import the configuration from the file.
  2. Create a Membership Service Provider (MSP) client from the Fabric SDK context.
  3. Use the MSP Client to register the user, declaring the user's attributes as we do so. A user attributes is of a key-value format. Here, we're giving the user a registered attribute with a value of true. This is the attribute that will be asserted in the chaincode when calling the Borrow function.
  4. Enroll the user. Once a user is registered, it must also be enrolled. This second steps is required to put the user into an 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.

  1. Create signing identity from registered and enrolled user.
  2. Create a channel context with the user. Declaring that this user is using this channel.
  3. Create a channel client from the channel context.
  4. Use the channel client to execute the chaincode function invocation request.
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


Conclusion

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.