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