Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Tests\user\Unit;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Access\AccessResult;
|
Chris@0
|
6 use Drupal\Core\Cache\Context\CacheContextsManager;
|
Chris@0
|
7 use Drupal\Core\DependencyInjection\Container;
|
Chris@0
|
8 use Drupal\Tests\UnitTestCase;
|
Chris@0
|
9 use Drupal\user\UserAccessControlHandler;
|
Chris@0
|
10
|
Chris@0
|
11 /**
|
Chris@0
|
12 * Tests the user access controller.
|
Chris@0
|
13 *
|
Chris@0
|
14 * @group Drupal
|
Chris@0
|
15 * @group User
|
Chris@0
|
16 *
|
Chris@0
|
17 * @coversDefaultClass \Drupal\user\UserAccessControlHandler
|
Chris@0
|
18 */
|
Chris@0
|
19 class UserAccessControlHandlerTest extends UnitTestCase {
|
Chris@0
|
20
|
Chris@0
|
21 /**
|
Chris@0
|
22 * The user access controller to test.
|
Chris@0
|
23 *
|
Chris@0
|
24 * @var \Drupal\user\UserAccessControlHandler
|
Chris@0
|
25 */
|
Chris@0
|
26 protected $accessControlHandler;
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * The mock user account with view access.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @var \Drupal\Core\Session\AccountInterface
|
Chris@0
|
32 */
|
Chris@0
|
33 protected $viewer;
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * The mock user account that is able to change their own account name.
|
Chris@0
|
37 *
|
Chris@0
|
38 * @var \Drupal\Core\Session\AccountInterface
|
Chris@0
|
39 */
|
Chris@0
|
40 protected $owner;
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * The mock administrative test user.
|
Chris@0
|
44 *
|
Chris@0
|
45 * @var \Drupal\Core\Session\AccountInterface
|
Chris@0
|
46 */
|
Chris@0
|
47 protected $admin;
|
Chris@0
|
48
|
Chris@0
|
49 /**
|
Chris@0
|
50 * The mocked test field items.
|
Chris@0
|
51 *
|
Chris@0
|
52 * @var \Drupal\Core\Field\FieldItemList
|
Chris@0
|
53 */
|
Chris@0
|
54 protected $items;
|
Chris@0
|
55
|
Chris@0
|
56 /**
|
Chris@0
|
57 * {@inheritdoc}
|
Chris@0
|
58 */
|
Chris@0
|
59 protected function setUp() {
|
Chris@0
|
60 parent::setUp();
|
Chris@0
|
61
|
Chris@0
|
62 $cache_contexts_manager = $this->prophesize(CacheContextsManager::class);
|
Chris@0
|
63 $cache_contexts_manager->assertValidTokens()->willReturn(TRUE);
|
Chris@0
|
64 $cache_contexts_manager->reveal();
|
Chris@0
|
65 $container = new Container();
|
Chris@0
|
66 $container->set('cache_contexts_manager', $cache_contexts_manager);
|
Chris@0
|
67 \Drupal::setContainer($container);
|
Chris@0
|
68
|
Chris@0
|
69 $this->viewer = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
Chris@0
|
70 $this->viewer
|
Chris@0
|
71 ->expects($this->any())
|
Chris@0
|
72 ->method('hasPermission')
|
Chris@0
|
73 ->will($this->returnValue(FALSE));
|
Chris@0
|
74 $this->viewer
|
Chris@0
|
75 ->expects($this->any())
|
Chris@0
|
76 ->method('id')
|
Chris@0
|
77 ->will($this->returnValue(1));
|
Chris@0
|
78
|
Chris@0
|
79 $this->owner = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
Chris@0
|
80 $this->owner
|
Chris@0
|
81 ->expects($this->any())
|
Chris@0
|
82 ->method('hasPermission')
|
Chris@0
|
83 ->will($this->returnValueMap([
|
Chris@0
|
84 ['administer users', FALSE],
|
Chris@0
|
85 ['change own username', TRUE],
|
Chris@0
|
86 ]));
|
Chris@0
|
87
|
Chris@0
|
88 $this->owner
|
Chris@0
|
89 ->expects($this->any())
|
Chris@0
|
90 ->method('id')
|
Chris@0
|
91 ->will($this->returnValue(2));
|
Chris@0
|
92
|
Chris@0
|
93 $this->admin = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
Chris@0
|
94 $this->admin
|
Chris@0
|
95 ->expects($this->any())
|
Chris@0
|
96 ->method('hasPermission')
|
Chris@0
|
97 ->will($this->returnValue(TRUE));
|
Chris@0
|
98
|
Chris@0
|
99 $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
Chris@0
|
100
|
Chris@0
|
101 $this->accessControlHandler = new UserAccessControlHandler($entity_type);
|
Chris@0
|
102 $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
Chris@0
|
103 $module_handler->expects($this->any())
|
Chris@0
|
104 ->method('getImplementations')
|
Chris@0
|
105 ->will($this->returnValue([]));
|
Chris@0
|
106 $this->accessControlHandler->setModuleHandler($module_handler);
|
Chris@0
|
107
|
Chris@0
|
108 $this->items = $this->getMockBuilder('Drupal\Core\Field\FieldItemList')
|
Chris@0
|
109 ->disableOriginalConstructor()
|
Chris@0
|
110 ->getMock();
|
Chris@0
|
111 $this->items
|
Chris@0
|
112 ->expects($this->any())
|
Chris@0
|
113 ->method('defaultAccess')
|
Chris@0
|
114 ->will($this->returnValue(AccessResult::allowed()));
|
Chris@0
|
115 }
|
Chris@0
|
116
|
Chris@0
|
117 /**
|
Chris@0
|
118 * Asserts correct field access grants for a field.
|
Chris@0
|
119 */
|
Chris@0
|
120 public function assertFieldAccess($field, $viewer, $target, $view, $edit) {
|
Chris@0
|
121 $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
Chris@0
|
122 $field_definition->expects($this->any())
|
Chris@0
|
123 ->method('getName')
|
Chris@0
|
124 ->will($this->returnValue($field));
|
Chris@0
|
125
|
Chris@0
|
126 $this->items
|
Chris@0
|
127 ->expects($this->any())
|
Chris@0
|
128 ->method('getEntity')
|
Chris@0
|
129 ->will($this->returnValue($this->{$target}));
|
Chris@0
|
130
|
Chris@0
|
131 foreach (['view' => $view, 'edit' => $edit] as $operation => $result) {
|
Chris@0
|
132 $result_text = !isset($result) ? 'null' : ($result ? 'true' : 'false');
|
Chris@0
|
133 $message = "User '$field' field access returns '$result_text' with operation '$operation' for '$viewer' accessing '$target'";
|
Chris@0
|
134 $this->assertSame($result, $this->accessControlHandler->fieldAccess($operation, $field_definition, $this->{$viewer}, $this->items), $message);
|
Chris@0
|
135 }
|
Chris@0
|
136 }
|
Chris@0
|
137
|
Chris@0
|
138 /**
|
Chris@0
|
139 * Ensures user name access is working properly.
|
Chris@0
|
140 *
|
Chris@0
|
141 * @dataProvider userNameProvider
|
Chris@0
|
142 */
|
Chris@0
|
143 public function testUserNameAccess($viewer, $target, $view, $edit) {
|
Chris@0
|
144 $this->assertFieldAccess('name', $viewer, $target, $view, $edit);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 /**
|
Chris@0
|
148 * Provides test data for testUserNameAccess().
|
Chris@0
|
149 */
|
Chris@0
|
150 public function userNameProvider() {
|
Chris@0
|
151 $name_access = [
|
Chris@0
|
152 // The viewer user is allowed to see user names on all accounts.
|
Chris@0
|
153 [
|
Chris@0
|
154 'viewer' => 'viewer',
|
Chris@0
|
155 'target' => 'viewer',
|
Chris@0
|
156 'view' => TRUE,
|
Chris@0
|
157 'edit' => FALSE,
|
Chris@0
|
158 ],
|
Chris@0
|
159 [
|
Chris@0
|
160 'viewer' => 'owner',
|
Chris@0
|
161 'target' => 'viewer',
|
Chris@0
|
162 'view' => TRUE,
|
Chris@0
|
163 'edit' => FALSE,
|
Chris@0
|
164 ],
|
Chris@0
|
165 [
|
Chris@0
|
166 'viewer' => 'viewer',
|
Chris@0
|
167 'target' => 'owner',
|
Chris@0
|
168 'view' => TRUE,
|
Chris@0
|
169 'edit' => FALSE,
|
Chris@0
|
170 ],
|
Chris@0
|
171 // The owner user is allowed to change its own user name.
|
Chris@0
|
172 [
|
Chris@0
|
173 'viewer' => 'owner',
|
Chris@0
|
174 'target' => 'owner',
|
Chris@0
|
175 'view' => TRUE,
|
Chris@0
|
176 'edit' => TRUE,
|
Chris@0
|
177 ],
|
Chris@0
|
178 // The users-administrator user has full access.
|
Chris@0
|
179 [
|
Chris@0
|
180 'viewer' => 'admin',
|
Chris@0
|
181 'target' => 'owner',
|
Chris@0
|
182 'view' => TRUE,
|
Chris@0
|
183 'edit' => TRUE,
|
Chris@0
|
184 ],
|
Chris@0
|
185 ];
|
Chris@0
|
186 return $name_access;
|
Chris@0
|
187 }
|
Chris@0
|
188
|
Chris@0
|
189 /**
|
Chris@0
|
190 * Tests that private user settings cannot be viewed by other users.
|
Chris@0
|
191 *
|
Chris@0
|
192 * @dataProvider hiddenUserSettingsProvider
|
Chris@0
|
193 */
|
Chris@0
|
194 public function testHiddenUserSettings($field, $viewer, $target, $view, $edit) {
|
Chris@0
|
195 $this->assertFieldAccess($field, $viewer, $target, $view, $edit);
|
Chris@0
|
196 }
|
Chris@0
|
197
|
Chris@0
|
198 /**
|
Chris@0
|
199 * Provides test data for testHiddenUserSettings().
|
Chris@0
|
200 */
|
Chris@0
|
201 public function hiddenUserSettingsProvider() {
|
Chris@0
|
202 $access_info = [];
|
Chris@0
|
203
|
Chris@0
|
204 $fields = [
|
Chris@0
|
205 'preferred_langcode',
|
Chris@0
|
206 'preferred_admin_langcode',
|
Chris@0
|
207 'timezone',
|
Chris@0
|
208 'mail',
|
Chris@0
|
209 ];
|
Chris@0
|
210
|
Chris@0
|
211 foreach ($fields as $field) {
|
Chris@0
|
212 $access_info[] = [
|
Chris@0
|
213 'field' => $field,
|
Chris@0
|
214 'viewer' => 'viewer',
|
Chris@0
|
215 'target' => 'viewer',
|
Chris@0
|
216 'view' => TRUE,
|
Chris@0
|
217 'edit' => TRUE,
|
Chris@0
|
218 ];
|
Chris@0
|
219 $access_info[] = [
|
Chris@0
|
220 'field' => $field,
|
Chris@0
|
221 'viewer' => 'viewer',
|
Chris@0
|
222 'target' => 'owner',
|
Chris@0
|
223 'view' => FALSE,
|
Chris@0
|
224 // Anyone with edit access to the user can also edit these fields. In
|
Chris@0
|
225 // reality edit access will already be checked on entity level and the
|
Chris@0
|
226 // user without view access will typically not be able to edit.
|
Chris@0
|
227 'edit' => TRUE,
|
Chris@0
|
228 ];
|
Chris@0
|
229 $access_info[] = [
|
Chris@0
|
230 'field' => $field,
|
Chris@0
|
231 'viewer' => 'owner',
|
Chris@0
|
232 'target' => 'owner',
|
Chris@0
|
233 'view' => TRUE,
|
Chris@0
|
234 'edit' => TRUE,
|
Chris@0
|
235 ];
|
Chris@0
|
236 $access_info[] = [
|
Chris@0
|
237 'field' => $field,
|
Chris@0
|
238 'viewer' => 'admin',
|
Chris@0
|
239 'target' => 'owner',
|
Chris@0
|
240 'view' => TRUE,
|
Chris@0
|
241 'edit' => TRUE,
|
Chris@0
|
242 ];
|
Chris@0
|
243 }
|
Chris@0
|
244
|
Chris@0
|
245 return $access_info;
|
Chris@0
|
246 }
|
Chris@0
|
247
|
Chris@0
|
248 /**
|
Chris@0
|
249 * Tests that private user settings cannot be viewed by other users.
|
Chris@0
|
250 *
|
Chris@0
|
251 * @dataProvider adminFieldAccessProvider
|
Chris@0
|
252 */
|
Chris@0
|
253 public function testAdminFieldAccess($field, $viewer, $target, $view, $edit) {
|
Chris@0
|
254 $this->assertFieldAccess($field, $viewer, $target, $view, $edit);
|
Chris@0
|
255 }
|
Chris@0
|
256
|
Chris@0
|
257 /**
|
Chris@0
|
258 * Provides test data for testAdminFieldAccess().
|
Chris@0
|
259 */
|
Chris@0
|
260 public function adminFieldAccessProvider() {
|
Chris@0
|
261 $access_info = [];
|
Chris@0
|
262
|
Chris@0
|
263 $fields = [
|
Chris@0
|
264 'roles',
|
Chris@0
|
265 'status',
|
Chris@0
|
266 'access',
|
Chris@0
|
267 'login',
|
Chris@0
|
268 'init',
|
Chris@0
|
269 ];
|
Chris@0
|
270
|
Chris@0
|
271 foreach ($fields as $field) {
|
Chris@0
|
272 $access_info[] = [
|
Chris@0
|
273 'field' => $field,
|
Chris@0
|
274 'viewer' => 'viewer',
|
Chris@0
|
275 'target' => 'viewer',
|
Chris@0
|
276 'view' => FALSE,
|
Chris@0
|
277 'edit' => FALSE,
|
Chris@0
|
278 ];
|
Chris@0
|
279 $access_info[] = [
|
Chris@0
|
280 'field' => $field,
|
Chris@0
|
281 'viewer' => 'viewer',
|
Chris@0
|
282 'target' => 'owner',
|
Chris@0
|
283 'view' => FALSE,
|
Chris@0
|
284 'edit' => FALSE,
|
Chris@0
|
285 ];
|
Chris@0
|
286 $access_info[] = [
|
Chris@0
|
287 'field' => $field,
|
Chris@0
|
288 'viewer' => 'admin',
|
Chris@0
|
289 'target' => 'owner',
|
Chris@0
|
290 'view' => TRUE,
|
Chris@0
|
291 'edit' => TRUE,
|
Chris@0
|
292 ];
|
Chris@0
|
293 }
|
Chris@0
|
294
|
Chris@0
|
295 return $access_info;
|
Chris@0
|
296 }
|
Chris@0
|
297
|
Chris@0
|
298 /**
|
Chris@0
|
299 * Tests that passwords cannot be viewed, just edited.
|
Chris@0
|
300 *
|
Chris@0
|
301 * @dataProvider passwordAccessProvider
|
Chris@0
|
302 */
|
Chris@0
|
303 public function testPasswordAccess($viewer, $target, $view, $edit) {
|
Chris@0
|
304 $this->assertFieldAccess('pass', $viewer, $target, $view, $edit);
|
Chris@0
|
305 }
|
Chris@0
|
306
|
Chris@0
|
307 /**
|
Chris@0
|
308 * Provides test data for passwordAccessProvider().
|
Chris@0
|
309 */
|
Chris@0
|
310 public function passwordAccessProvider() {
|
Chris@0
|
311 $pass_access = [
|
Chris@0
|
312 [
|
Chris@0
|
313 'viewer' => 'viewer',
|
Chris@0
|
314 'target' => 'viewer',
|
Chris@0
|
315 'view' => FALSE,
|
Chris@0
|
316 'edit' => TRUE,
|
Chris@0
|
317 ],
|
Chris@0
|
318 [
|
Chris@0
|
319 'viewer' => 'viewer',
|
Chris@0
|
320 'target' => 'owner',
|
Chris@0
|
321 'view' => FALSE,
|
Chris@0
|
322 // Anyone with edit access to the user can also edit these fields. In
|
Chris@0
|
323 // reality edit access will already be checked on entity level and the
|
Chris@0
|
324 // user without view access will typically not be able to edit.
|
Chris@0
|
325 'edit' => TRUE,
|
Chris@0
|
326 ],
|
Chris@0
|
327 [
|
Chris@0
|
328 'viewer' => 'owner',
|
Chris@0
|
329 'target' => 'viewer',
|
Chris@0
|
330 'view' => FALSE,
|
Chris@0
|
331 'edit' => TRUE,
|
Chris@0
|
332 ],
|
Chris@0
|
333 [
|
Chris@0
|
334 'viewer' => 'admin',
|
Chris@0
|
335 'target' => 'owner',
|
Chris@0
|
336 'view' => FALSE,
|
Chris@0
|
337 'edit' => TRUE,
|
Chris@0
|
338 ],
|
Chris@0
|
339 ];
|
Chris@0
|
340 return $pass_access;
|
Chris@0
|
341 }
|
Chris@0
|
342
|
Chris@0
|
343 /**
|
Chris@0
|
344 * Tests the user created field access.
|
Chris@0
|
345 *
|
Chris@0
|
346 * @dataProvider createdAccessProvider
|
Chris@0
|
347 */
|
Chris@0
|
348 public function testCreatedAccess($viewer, $target, $view, $edit) {
|
Chris@0
|
349 $this->assertFieldAccess('created', $viewer, $target, $view, $edit);
|
Chris@0
|
350 }
|
Chris@0
|
351
|
Chris@0
|
352 /**
|
Chris@0
|
353 * Provides test data for testCreatedAccess().
|
Chris@0
|
354 */
|
Chris@0
|
355 public function createdAccessProvider() {
|
Chris@0
|
356 $created_access = [
|
Chris@0
|
357 [
|
Chris@0
|
358 'viewer' => 'viewer',
|
Chris@0
|
359 'target' => 'viewer',
|
Chris@0
|
360 'view' => TRUE,
|
Chris@0
|
361 'edit' => FALSE,
|
Chris@0
|
362 ],
|
Chris@0
|
363 [
|
Chris@0
|
364 'viewer' => 'owner',
|
Chris@0
|
365 'target' => 'viewer',
|
Chris@0
|
366 'view' => TRUE,
|
Chris@0
|
367 'edit' => FALSE,
|
Chris@0
|
368 ],
|
Chris@0
|
369 [
|
Chris@0
|
370 'viewer' => 'admin',
|
Chris@0
|
371 'target' => 'owner',
|
Chris@0
|
372 'view' => TRUE,
|
Chris@0
|
373 'edit' => TRUE,
|
Chris@0
|
374 ],
|
Chris@0
|
375 ];
|
Chris@0
|
376 return $created_access;
|
Chris@0
|
377 }
|
Chris@0
|
378
|
Chris@0
|
379 /**
|
Chris@0
|
380 * Tests access to a non-existing base field.
|
Chris@0
|
381 *
|
Chris@0
|
382 * @dataProvider NonExistingFieldAccessProvider
|
Chris@0
|
383 */
|
Chris@0
|
384 public function testNonExistingFieldAccess($viewer, $target, $view, $edit) {
|
Chris@0
|
385 // By default everyone has access to all fields that do not have explicit
|
Chris@0
|
386 // access control.
|
Chris@0
|
387 // @see EntityAccessControlHandler::checkFieldAccess()
|
Chris@0
|
388 $this->assertFieldAccess('some_non_existing_field', $viewer, $target, $view, $edit);
|
Chris@0
|
389 }
|
Chris@0
|
390
|
Chris@0
|
391 /**
|
Chris@0
|
392 * Provides test data for testNonExistingFieldAccess().
|
Chris@0
|
393 */
|
Chris@0
|
394 public function NonExistingFieldAccessProvider() {
|
Chris@0
|
395 $created_access = [
|
Chris@0
|
396 [
|
Chris@0
|
397 'viewer' => 'viewer',
|
Chris@0
|
398 'target' => 'viewer',
|
Chris@0
|
399 'view' => TRUE,
|
Chris@0
|
400 'edit' => TRUE,
|
Chris@0
|
401 ],
|
Chris@0
|
402 [
|
Chris@0
|
403 'viewer' => 'owner',
|
Chris@0
|
404 'target' => 'viewer',
|
Chris@0
|
405 'view' => TRUE,
|
Chris@0
|
406 'edit' => TRUE,
|
Chris@0
|
407 ],
|
Chris@0
|
408 [
|
Chris@0
|
409 'viewer' => 'admin',
|
Chris@0
|
410 'target' => 'owner',
|
Chris@0
|
411 'view' => TRUE,
|
Chris@0
|
412 'edit' => TRUE,
|
Chris@0
|
413 ],
|
Chris@0
|
414 ];
|
Chris@0
|
415 return $created_access;
|
Chris@0
|
416 }
|
Chris@0
|
417
|
Chris@0
|
418 }
|