11

How to Use OpenZeppelin’s New AccessControl Contract

 3 years ago
source link: https://medium.com/better-programming/how-to-use-openzeppelins-new-accesscontrol-contract-5b49a4bcd160
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to Use OpenZeppelin’s New AccessControl Contract

Using the latest version of the gold standard smart contract library

Image for post
Image for post
OpenZeppelin

Version 3 of OpenZeppelin’s Smart Contracts library is out! In the latest release, they introduce a brand new method of controlling access to functions.

What We Know

Controlling access to certain functions is paramount to ensuring the security of smart contracts, and has been the case in Solidity since the introduction of the Ethereum Virtual Machine.

Developers familiar with OpenZeppelin’s Smart Contract repository know that it already provides options to restrict functionality depending on access level.

The most common is the onlyOwner pattern, administered by the Ownable contract. Another is Openzeppelin’s Roles contract, which enables contracts to define multiple roles before deployment and set rules in each function, ensuring that msg.sender holds the correct role.

Ownable

The onlyOwner pattern is the most commonly used and easily implemented access control method. It’s primitive but highly effective.

It assumes that there is a single administrator of the smart contract, and enables the administrator to transfer ownership to another address.

Extending the Ownable contract allows child contracts to define functions with the onlyOwner custom modifier. These functions require that the sender of the transaction be the single administrator.

function normalFunction() public {
// anyone can call this
}function restrictedFunction() public onlyOwner {
// only the owner can call this
}

This is a simple example of how to utilize the custom modifier provided by the Ownable contract to restrict function access.

Roles

Despite the popularity and ease of use of the Ownable contract, other OpenZeppelin contracts in the repository only ever used the Roles library for access control. This is because of the flexibility that the Roles library provides over the rigidity of the Ownable contract.

Being a library, it is not extended by child contracts but is used as a tool for adding functionality to data types, with the using statement. The Roles library has three functions that it provides for the Role data type, which it defines itself.

Figure 1 shows the definition of Roles.

Figure 1: Roles.sol

At the top, you can see the Role struct. This is used by contracts to define multiple roles and their members. The functions add(), remove(), and has() are functions that the library uses to interact with a Role struct.

For example, figure 2 shows how a token might use two separate roles, _minters and _burners, to apply access restrictions to certain functions.

Figure 2: Implementing Roles

Notice how in the mint() function the require statement ensures that the sender of the message is a minter by using the _minters.has(msg.sender) function.

What’s New

Given that this has been the standard for a while, the big news for developers is that the Roles contract has been removed in the upgrade from version 2.5.x to 3.x.

Principles

The Roles library was somewhat restrictive in the functionality it provided.

Being a library, data storage must be controlled by the importing contract. Ideally, access control should be abstracted away to some degree, where the importing contract needs only worry about the restrictions on each function.

The new AccessControl contract is touted as:

“A one-stop-shop for all authorization needs. It lets you easily define multiple roles with different permissions, as well as which accounts are allowed to grant and revoke each role. It also boosts transparency by enabling enumeration of all privileged accounts in a system.”

The last two points in that statement were not possible with the Roles library.

OpenZeppelin looks to be moving towards a system that is more reminiscent of role-based access control (RBAC) and attribute-based access control (ABAC) standards, prominent in traditional computing security.

Dissecting the Code

Figure 3 shows the AccessControl contract code.

Figure 3: AccessControl definition

The RoleData struct on line 42 uses EnumerableSet (also new to version 3) as the data structure to store members. This allows easy iteration on privileged users.

The struct also stores adminRole as a bytes32 variable. This defines which role acts as administrator over a particular role (i.e. the role that has the ability to act as administrator over this role, granting and revoking this role to users).

Events are now emitted when a role is granted or revoked, defined on lines 57 and 66.

The Roles contract provided just three functions: has(), add(), and remove(). Forms of these exist in AccessControl, as well as extra functionality like getting role counts, getting specific members of a role by ID, and the ability to renounce a role.

How to Use It

Figure 2 gave the example of a token contract needing two separate roles, _minters and _burners, using the Roles library. For continuity, we’ll use the same concept and apply the AccessControl contract to do it.

Figure 4 shows the implementation of this.

Figure 4: Implementing AccessControl

So, what has changed? For a start, each role is no longer defined in the child contract, as they are stored in the parent. Only the bytes32 IDs exist as constant state variables in the child contract (MINTER_ROLE and BURNER_ROLE in this example).

The _setupRole() is used in the constructor to set the initial administrator of roles, bypassing checks performed by grantRole() in AccessControl (since at the point of construction there is no administrator yet).

Also, instead of calling library functions as extensions on data types (i.e. _minters.has(msg.sender)), the functions are internal in their own right (hasRole(MINTER_ROLE, msg.sender)). This makes the code in the child contract generally more clean and readable.

Abstracting away more of the functionality allows child contracts to build on top of the AccessControl contract easier than was possible with the Roles library. In a previous article, I proposed future implementations of RBAC and ABAC systems built using AccessControl .

If you’re interested in further exploring the possibilities, Alberto Cuesta Canada has produced examples of contracts expanding on the basic functionality provided by AccessControl.

Conclusion

The introduction of AccessControl is a big step forward in bringing the Ethereum ecosystem closer to industry standards with regards to system security.

The contract received heavy input from industry experts. I imagine some interesting and complex systems will arise from this contract soon, pushing the mantle even further.

I highly recommend importing OpenZeppelin contracts into your project and expanding on them yourself. You never know what you might learn or stumble upon!

Thanks for reading.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK