AccessManaged.sol 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {IAuthority, safeCanCall} from "./IAuthority.sol";
  4. import {IManaged} from "./IManaged.sol";
  5. import {Context} from "../../utils/Context.sol";
  6. /**
  7. * @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
  8. * permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
  9. * implementing a policy that allows certain callers to access certain functions.
  10. *
  11. * IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
  12. * functions, and ideally only used in `external` functions. See {restricted}.
  13. */
  14. abstract contract AccessManaged is Context, IManaged {
  15. address private _authority;
  16. /**
  17. * @dev Initializes the contract connected to an initial authority.
  18. */
  19. constructor(address initialAuthority) {
  20. _setAuthority(initialAuthority);
  21. }
  22. /**
  23. * @dev Restricts access to a function as defined by the connected Authority for this contract and the
  24. * caller and selector of the function that entered the contract.
  25. *
  26. * [IMPORTANT]
  27. * ====
  28. * In general, this modifier should only be used on `external` functions. It is okay to use it on `public` functions
  29. * that are used as external entry points and are not called internally. Unless you know what you're doing, it
  30. * should never be used on `internal` functions. Failure to follow these rules can have critical security
  31. * implications! This is because the permissions are determined by the function that entered the contract, i.e. the
  32. * function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
  33. * ====
  34. *
  35. * [NOTE]
  36. * ====
  37. * Selector collisions are mitigated by scoping permissions per contract, but some edge cases must be considered:
  38. *
  39. * * If the https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function[`receive()`] function is restricted,
  40. * any other function with a `0x00000000` selector will share permissions with `receive()`.
  41. * * Similarly, if there's no `receive()` function but a `fallback()` instead, the fallback might be called with empty `calldata`,
  42. * sharing the `0x00000000` selector permissions as well.
  43. * * For any other selector, if the restricted function is set on an upgradeable contract, an upgrade may remove the restricted
  44. * function and replace it with a new method whose selector replaces the last one, keeping the previous permissions.
  45. * ====
  46. */
  47. modifier restricted() {
  48. _checkCanCall(_msgSender(), address(this), msg.sig);
  49. _;
  50. }
  51. /**
  52. * @dev Returns the current authority.
  53. */
  54. function authority() public view virtual returns (address) {
  55. return _authority;
  56. }
  57. /**
  58. * @dev Transfers control to a new authority. The caller must be the current authority.
  59. */
  60. function setAuthority(address newAuthority) public virtual {
  61. address caller = _msgSender();
  62. if (caller != authority()) {
  63. revert AccessManagedUnauthorized(caller);
  64. }
  65. if (newAuthority.code.length == 0) {
  66. revert AccessManagedInvalidAuthority(newAuthority);
  67. }
  68. _setAuthority(newAuthority);
  69. }
  70. /**
  71. * @dev Transfers control to a new authority. Internal function with no access restriction.
  72. */
  73. function _setAuthority(address newAuthority) internal virtual {
  74. _authority = newAuthority;
  75. emit AuthorityUpdated(newAuthority);
  76. }
  77. /**
  78. * @dev Reverts if the caller is not allowed to call the function identified by a selector.
  79. */
  80. function _checkCanCall(address caller, address target, bytes4 selector) internal view virtual {
  81. (bool allowed, uint32 delay) = safeCanCall(authority(), caller, target, selector);
  82. if (!allowed) {
  83. if (delay > 0) {
  84. revert AccessManagedRequiredDelay(caller, delay);
  85. } else {
  86. revert AccessManagedUnauthorized(caller);
  87. }
  88. }
  89. }
  90. }