Example Walkthough Using the Fine-Grained Access Control Library

This topic provides some examples of how the library and chaincode can be used. These examples all assume that the Init() function has been called to create the bootstrap entities and the caller of Init() and invoke() is "%CN%frank.thomas@example.com". The normal flow in an application is to create some initial access control lists that will be used to grant or deny access to the other entities.

Initialization

Call the Initialization() function to create bootstrap entities when deploying chaincodes. For example:

import "chaincodeACL"
func (t \*SimpleChaincode) Init(nil, stub shim.ChaincodeStubInterface) pb.Response
{
         err := chaincodeACL.Initialization(stub)
}

Create an ACL

import "chaincodeACL"
...
{

**ACLMgr**  := chaincodeACL.NewACLManager(nil, stub) // Not specify identity, use caller's identity as default.

// Define a new ACL
**newACL**  := chaincodeACL.ACL{

    "AllowAdmins",   // ACL name
    "Allow administrators full access",  // Description
    []string{"CREATE","READ","UPDATE","DELETE"},    // Accesses allowed or not
    true, // Allowed
    []string{"%CN%bob.dole@example.com","%OU%example.com,"%GRP%admins"}, // Initial identity patterns
    ".ACLs.acl", // Start with bootstrap ACL

}

// Add this ACL with default identity (caller's identify here)
err :=  **ACLMgr**.Create( **newACL** , nil)

}

You can use the new ACL to modify who can perform certain operations. First, add this new ACL to the bootstrap group .Groups to allow any administrator to create a group.

Add an ACL to a group

import "chaincodeACL"
…
{

  **groupMgr**  := chaincodeACL.NewGroupManager(nil, stub) // Not specify identity, use caller's identity as default.
  err :=  **groupMgr**.AddAfterACL(

    ".Groups",     // Bootstrap group name
    ".Groups.ACL", // Which ACL to add after
    "AllowAdmins", // The new ACL to add
    nil            // with default identity that's frank.thomas

)

}

This adds the AllowAdmins ACL to the bootstrap group .Groups after the initial bootstrap ACL. Thus this ensures that Frank Thomas can still complete operations on the .Groups group because the ACL that grants Frank permission is first in the list. Now, anyone who matches the AllowAdmins ACL can complete CREATE, READ, UPDATE, or DELETE operations (they can now create groups).

Create a group

Administrators can now create a group.

import "chaincodeACL"
...
{

...
  // Define a new group.
  **newGroup**  := chaincodeACL.Group{

      "AdminGrp",   // Name of the group
      "Administrators of the app",   // Description of the group
      {"%CN%jill.muller@example.com","%CN%ivan.novak@example.com","%ATTR%role=admin"},
      []string{"AllowAdmins"},   // The ACL for the group

    }

  **groupMgr**  := chaincodeACL.NewGroupManager(nil, stub)   // Not specify identity, use caller's identity as default.
  err :=  **groupMgr**.Create( **newGroup** , bob\_garcia\_certificate)   // Using a specific certificate

...
}

This call is using an explicit identity (Bob Garcia, by using his certificate) to attempt to create a group. Because Bob Garcia matches a pattern in the AllowAdmins ACL and members of that ACL can perform CREATE operations on the bootstrap group .Groups, this call will succeed. Had Jim Silva, who was not in organization unit example.com nor in the group AdminGrp (which still doesn’t exist), had his certificate passed as the last argument, the call would fail as he doesn’t have the appropriate permissions. This call creates a group called "AdminGrp" with initial members of the group being jill.muller@example.com and ivan.novak@example.com or anyone with the attribute (ABAC) role=admin.

Create a resource

import "chaincodeACL"
...
{

  ...
  **newResource**  :=  **chaincodeACL**.Resource{

      "transferMarble", // Name of resource to create

      "The transferMarble chaincode function", // Description of the resource

      []string{"AllowAdmins"}, // Single ACL for now allowing administrators

  }

  **resourceMgr**  :=  **chaincodeACL**.NewResourceManager(nil, stub)  // Not specify identity, use caller's identity as default.
  err :=  **resourceMgr**.Create(resourceMgr, nil)   // Using caller's certificate

  ...
}
This creates a resource named transferMarble that the application might use to control access to the transferMarble chaincode function. The access is currently limited by the AllowAdmins access control list.

Check access for a resource

You can use this new resource in your chaincode to allow only administators to transfer a marble, by modifying the invoke() method of the Marbles chaincode as shown in the following code:

import "chaincodeACL"
…
func (t \*SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {

  **resourceMgr**  :=  **chaincodeACL**.NewResourceManager(nil, stub)   // Not specify identity, use caller's identity as default.

  function, args := stub.GetFunctionAndParameters()

  fmt.Println("invoke is running " + function)        // Handle different functions

  if function == "initMarble" {   //create a marble

      return t.initMarble(stub, args)}

  else if function == " **transferMarble**" { //change owner of a specific marble

    **allowed** , err : =  **resourceMgr**. **CheckAccess** ("transferMarble", "UPDATE", nil)
    if  **allowed**  == true {

      return t.transferMarble(stub, args)

    else {

      return NOACCESS

    }

    } else if function == "transferMarblesBasedOnColor" { //transfer all marbles of a certain color
    …

    }

}