MaskBuilder.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Security\Acl\Permission;
  11. /**
  12. * This class allows you to build cumulative permissions easily, or convert
  13. * masks to a human-readable format.
  14. *
  15. * <code>
  16. * $builder = new MaskBuilder();
  17. * $builder
  18. * ->add('view')
  19. * ->add('create')
  20. * ->add('edit')
  21. * ;
  22. * var_dump($builder->get()); // int(7)
  23. * var_dump($builder->getPattern()); // string(32) ".............................ECV"
  24. * </code>
  25. *
  26. * We have defined some commonly used base permissions which you can use:
  27. * - VIEW: the SID is allowed to view the domain object / field
  28. * - CREATE: the SID is allowed to create new instances of the domain object / fields
  29. * - EDIT: the SID is allowed to edit existing instances of the domain object / field
  30. * - DELETE: the SID is allowed to delete domain objects
  31. * - UNDELETE: the SID is allowed to recover domain objects from trash
  32. * - OPERATOR: the SID is allowed to perform any action on the domain object
  33. * except for granting others permissions
  34. * - MASTER: the SID is allowed to perform any action on the domain object,
  35. * and is allowed to grant other SIDs any permission except for
  36. * MASTER and OWNER permissions
  37. * - OWNER: the SID is owning the domain object in question and can perform any
  38. * action on the domain object as well as grant any permission
  39. *
  40. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  41. */
  42. class MaskBuilder
  43. {
  44. const MASK_VIEW = 1; // 1 << 0
  45. const MASK_CREATE = 2; // 1 << 1
  46. const MASK_EDIT = 4; // 1 << 2
  47. const MASK_DELETE = 8; // 1 << 3
  48. const MASK_UNDELETE = 16; // 1 << 4
  49. const MASK_OPERATOR = 32; // 1 << 5
  50. const MASK_MASTER = 64; // 1 << 6
  51. const MASK_OWNER = 128; // 1 << 7
  52. const MASK_IDDQD = 1073741823; // 1 << 0 | 1 << 1 | ... | 1 << 30
  53. const CODE_VIEW = 'V';
  54. const CODE_CREATE = 'C';
  55. const CODE_EDIT = 'E';
  56. const CODE_DELETE = 'D';
  57. const CODE_UNDELETE = 'U';
  58. const CODE_OPERATOR = 'O';
  59. const CODE_MASTER = 'M';
  60. const CODE_OWNER = 'N';
  61. const ALL_OFF = '................................';
  62. const OFF = '.';
  63. const ON = '*';
  64. private $mask;
  65. /**
  66. * Constructor
  67. *
  68. * @param integer $mask optional; defaults to 0
  69. *
  70. * @throws \InvalidArgumentException
  71. */
  72. public function __construct($mask = 0)
  73. {
  74. if (!is_int($mask)) {
  75. throw new \InvalidArgumentException('$mask must be an integer.');
  76. }
  77. $this->mask = $mask;
  78. }
  79. /**
  80. * Adds a mask to the permission
  81. *
  82. * @param mixed $mask
  83. *
  84. * @return MaskBuilder
  85. *
  86. * @throws \InvalidArgumentException
  87. */
  88. public function add($mask)
  89. {
  90. if (is_string($mask) && defined($name = 'static::MASK_'.strtoupper($mask))) {
  91. $mask = constant($name);
  92. } elseif (!is_int($mask)) {
  93. throw new \InvalidArgumentException('$mask must be an integer.');
  94. }
  95. $this->mask |= $mask;
  96. return $this;
  97. }
  98. /**
  99. * Returns the mask of this permission
  100. *
  101. * @return integer
  102. */
  103. public function get()
  104. {
  105. return $this->mask;
  106. }
  107. /**
  108. * Returns a human-readable representation of the permission
  109. *
  110. * @return string
  111. */
  112. public function getPattern()
  113. {
  114. $pattern = self::ALL_OFF;
  115. $length = strlen($pattern);
  116. $bitmask = str_pad(decbin($this->mask), $length, '0', STR_PAD_LEFT);
  117. for ($i=$length-1; $i>=0; $i--) {
  118. if ('1' === $bitmask[$i]) {
  119. try {
  120. $pattern[$i] = self::getCode(1 << ($length - $i - 1));
  121. } catch (\Exception $notPredefined) {
  122. $pattern[$i] = self::ON;
  123. }
  124. }
  125. }
  126. return $pattern;
  127. }
  128. /**
  129. * Removes a mask from the permission
  130. *
  131. * @param mixed $mask
  132. *
  133. * @return MaskBuilder
  134. *
  135. * @throws \InvalidArgumentException
  136. */
  137. public function remove($mask)
  138. {
  139. if (is_string($mask) && defined($name = 'static::MASK_'.strtoupper($mask))) {
  140. $mask = constant($name);
  141. } elseif (!is_int($mask)) {
  142. throw new \InvalidArgumentException('$mask must be an integer.');
  143. }
  144. $this->mask &= ~$mask;
  145. return $this;
  146. }
  147. /**
  148. * Resets the PermissionBuilder
  149. *
  150. * @return MaskBuilder
  151. */
  152. public function reset()
  153. {
  154. $this->mask = 0;
  155. return $this;
  156. }
  157. /**
  158. * Returns the code for the passed mask
  159. *
  160. * @param integer $mask
  161. * @throws \InvalidArgumentException
  162. * @throws \RuntimeException
  163. * @return string
  164. */
  165. public static function getCode($mask)
  166. {
  167. if (!is_int($mask)) {
  168. throw new \InvalidArgumentException('$mask must be an integer.');
  169. }
  170. $reflection = new \ReflectionClass(get_called_class());
  171. foreach ($reflection->getConstants() as $name => $cMask) {
  172. if (0 !== strpos($name, 'MASK_')) {
  173. continue;
  174. }
  175. if ($mask === $cMask) {
  176. if (!defined($cName = 'static::CODE_'.substr($name, 5))) {
  177. throw new \RuntimeException('There was no code defined for this mask.');
  178. }
  179. return constant($cName);
  180. }
  181. }
  182. throw new \InvalidArgumentException(sprintf('The mask "%d" is not supported.', $mask));
  183. }
  184. }