Example Walkthough Using the Fine-Grained Access Control Library

This topic provides some examples of how this library and chaincode can be used. These all assuming Init() 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 will be to create some initial access control lists that will be used to grant or deny access to the other entities.

Initialization

Call Initialization() to create bootstrap entities when instantiating chaincode. For example:

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

Create a new 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 admins 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)

}

Now that we have a new ACL, we can use that to modify who can perform certain operations. So we’ll first add this new ACL to the bootstrap group .Groups to allow any admin 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 perform operations on .Groups as the ACL granting him permission is first in the list. But now anyone that matches the AllowAdmins ACL can perform CREATE, READ, UPDATE, or DELETE operations (they can now create new groups).

Create a new group

Admins can now create a new 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 - that of Bob Garcia (using his certificate) - to try and create a new group. Since 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 will create a new 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 new 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 admins

  }

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

  ...
}

This would create a new 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

We can use this new resource in our chaincode to only allow admins to transfer a marble by modifying the invoke() method of the Marbles chaincode as follows:

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 new 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
    …

    }

}