AccessControl.behavior.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
  4. const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
  5. const ROLE = web3.utils.soliditySha3('ROLE');
  6. const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE');
  7. function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, otherAdmin) {
  8. shouldSupportInterfaces(['AccessControl']);
  9. describe('default admin', function () {
  10. it('deployer has default admin role', async function () {
  11. expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);
  12. });
  13. it('other roles\'s admin is the default admin role', async function () {
  14. expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
  15. });
  16. it('default admin role\'s admin is itself', async function () {
  17. expect(await this.accessControl.getRoleAdmin(DEFAULT_ADMIN_ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
  18. });
  19. });
  20. describe('granting', function () {
  21. beforeEach(async function () {
  22. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  23. });
  24. it('non-admin cannot grant role to other accounts', async function () {
  25. await expectRevert(
  26. this.accessControl.grantRole(ROLE, authorized, { from: other }),
  27. `${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
  28. );
  29. });
  30. it('accounts can be granted a role multiple times', async function () {
  31. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  32. const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  33. expectEvent.notEmitted(receipt, 'RoleGranted');
  34. });
  35. });
  36. describe('revoking', function () {
  37. it('roles that are not had can be revoked', async function () {
  38. expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
  39. const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
  40. expectEvent.notEmitted(receipt, 'RoleRevoked');
  41. });
  42. context('with granted role', function () {
  43. beforeEach(async function () {
  44. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  45. });
  46. it('admin can revoke role', async function () {
  47. const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
  48. expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: admin });
  49. expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
  50. });
  51. it('non-admin cannot revoke role', async function () {
  52. await expectRevert(
  53. this.accessControl.revokeRole(ROLE, authorized, { from: other }),
  54. `${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`,
  55. );
  56. });
  57. it('a role can be revoked multiple times', async function () {
  58. await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
  59. const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin });
  60. expectEvent.notEmitted(receipt, 'RoleRevoked');
  61. });
  62. });
  63. });
  64. describe('renouncing', function () {
  65. it('roles that are not had can be renounced', async function () {
  66. const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
  67. expectEvent.notEmitted(receipt, 'RoleRevoked');
  68. });
  69. context('with granted role', function () {
  70. beforeEach(async function () {
  71. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  72. });
  73. it('bearer can renounce role', async function () {
  74. const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
  75. expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: authorized });
  76. expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false);
  77. });
  78. it('only the sender can renounce their roles', async function () {
  79. await expectRevert(
  80. this.accessControl.renounceRole(ROLE, authorized, { from: admin }),
  81. `${errorPrefix}: can only renounce roles for self`,
  82. );
  83. });
  84. it('a role can be renounced multiple times', async function () {
  85. await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
  86. const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized });
  87. expectEvent.notEmitted(receipt, 'RoleRevoked');
  88. });
  89. });
  90. });
  91. describe('setting role admin', function () {
  92. beforeEach(async function () {
  93. const receipt = await this.accessControl.setRoleAdmin(ROLE, OTHER_ROLE);
  94. expectEvent(receipt, 'RoleAdminChanged', {
  95. role: ROLE,
  96. previousAdminRole: DEFAULT_ADMIN_ROLE,
  97. newAdminRole: OTHER_ROLE,
  98. });
  99. await this.accessControl.grantRole(OTHER_ROLE, otherAdmin, { from: admin });
  100. });
  101. it('a role\'s admin role can be changed', async function () {
  102. expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(OTHER_ROLE);
  103. });
  104. it('the new admin can grant roles', async function () {
  105. const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
  106. expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: otherAdmin });
  107. });
  108. it('the new admin can revoke roles', async function () {
  109. await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin });
  110. const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: otherAdmin });
  111. expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: otherAdmin });
  112. });
  113. it('a role\'s previous admins no longer grant roles', async function () {
  114. await expectRevert(
  115. this.accessControl.grantRole(ROLE, authorized, { from: admin }),
  116. `${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`,
  117. );
  118. });
  119. it('a role\'s previous admins no longer revoke roles', async function () {
  120. await expectRevert(
  121. this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
  122. `${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`,
  123. );
  124. });
  125. });
  126. describe('onlyRole modifier', function () {
  127. beforeEach(async function () {
  128. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  129. });
  130. it('do not revert if sender has role', async function () {
  131. await this.accessControl.senderProtected(ROLE, { from: authorized });
  132. });
  133. it('revert if sender doesn\'t have role #1', async function () {
  134. await expectRevert(
  135. this.accessControl.senderProtected(ROLE, { from: other }),
  136. `${errorPrefix}: account ${other.toLowerCase()} is missing role ${ROLE}`,
  137. );
  138. });
  139. it('revert if sender doesn\'t have role #2', async function () {
  140. await expectRevert(
  141. this.accessControl.senderProtected(OTHER_ROLE, { from: authorized }),
  142. `${errorPrefix}: account ${authorized.toLowerCase()} is missing role ${OTHER_ROLE}`,
  143. );
  144. });
  145. });
  146. }
  147. function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
  148. shouldSupportInterfaces(['AccessControlEnumerable']);
  149. describe('enumerating', function () {
  150. it('role bearers can be enumerated', async function () {
  151. await this.accessControl.grantRole(ROLE, authorized, { from: admin });
  152. await this.accessControl.grantRole(ROLE, other, { from: admin });
  153. await this.accessControl.grantRole(ROLE, otherAuthorized, { from: admin });
  154. await this.accessControl.revokeRole(ROLE, other, { from: admin });
  155. const memberCount = await this.accessControl.getRoleMemberCount(ROLE);
  156. expect(memberCount).to.bignumber.equal('2');
  157. const bearers = [];
  158. for (let i = 0; i < memberCount; ++i) {
  159. bearers.push(await this.accessControl.getRoleMember(ROLE, i));
  160. }
  161. expect(bearers).to.have.members([authorized, otherAuthorized]);
  162. });
  163. it('role enumeration should be in sync after renounceRole call', async function () {
  164. expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
  165. await this.accessControl.grantRole(ROLE, admin, { from: admin });
  166. expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1');
  167. await this.accessControl.renounceRole(ROLE, admin, { from: admin });
  168. expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
  169. });
  170. });
  171. }
  172. module.exports = {
  173. shouldBehaveLikeAccessControl,
  174. shouldBehaveLikeAccessControlEnumerable,
  175. };