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