Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / user / tests / src / Unit / PermissionHandlerTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\user\Unit\PermissionHandlerTest.
6  */
7
8 namespace Drupal\Tests\user\Unit;
9
10 use Drupal\Core\Extension\Extension;
11 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
12 use Drupal\Core\StringTranslation\TranslatableMarkup;
13 use Drupal\Core\StringTranslation\TranslationInterface;
14 use Drupal\Tests\UnitTestCase;
15 use Drupal\user\PermissionHandler;
16 use org\bovigo\vfs\vfsStream;
17 use org\bovigo\vfs\vfsStreamDirectory;
18 use org\bovigo\vfs\vfsStreamWrapper;
19
20 /**
21  * Tests the permission handler.
22  *
23  * @group user
24  *
25  * @coversDefaultClass \Drupal\user\PermissionHandler
26  */
27 class PermissionHandlerTest extends UnitTestCase {
28
29   /**
30    * The tested permission handler.
31    *
32    * @var \Drupal\Tests\user\Unit\TestPermissionHandler|\Drupal\user\PermissionHandler
33    */
34   protected $permissionHandler;
35
36   /**
37    * The mocked module handler.
38    *
39    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
40    */
41   protected $moduleHandler;
42
43   /**
44    * The mocked string translation.
45    *
46    * @var \Drupal\Tests\user\Unit\TestTranslationManager
47    */
48   protected $stringTranslation;
49
50   /**
51    * The mocked controller resolver.
52    *
53    * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
54    */
55   protected $controllerResolver;
56
57   /**
58    * {@inheritdoc}
59    */
60   protected function setUp() {
61     parent::setUp();
62
63     $this->stringTranslation = new TestTranslationManager();
64     $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
65   }
66
67   /**
68    * Provides an extension object for a given module with a human name.
69    *
70    * @param string $module
71    *   The module machine name.
72    * @param string $name
73    *   The module human name.
74    *
75    * @return \Drupal\Core\Extension\Extension
76    *   The extension object.
77    */
78   protected function mockModuleExtension($module, $name) {
79     $extension = new Extension($this->root, $module, "modules/$module");
80     $extension->info['name'] = $name;
81     return $extension;
82   }
83
84   /**
85    * Tests permissions provided by YML files.
86    *
87    * @covers ::__construct
88    * @covers ::getPermissions
89    * @covers ::buildPermissionsYaml
90    * @covers ::moduleProvidesPermissions
91    */
92   public function testBuildPermissionsYaml() {
93     vfsStreamWrapper::register();
94     $root = new vfsStreamDirectory('modules');
95     vfsStreamWrapper::setRoot($root);
96
97     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
98     $this->moduleHandler->expects($this->once())
99       ->method('getModuleDirectories')
100       ->willReturn([
101         'module_a' => vfsStream::url('modules/module_a'),
102         'module_b' => vfsStream::url('modules/module_b'),
103         'module_c' => vfsStream::url('modules/module_c'),
104       ]);
105
106     $url = vfsStream::url('modules');
107     mkdir($url . '/module_a');
108     file_put_contents($url . '/module_a/module_a.permissions.yml', "access_module_a: single_description");
109     mkdir($url . '/module_b');
110     file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
111 'access module b':
112   title: 'Access B'
113   description: 'bla bla'
114 'access module a via module b':
115   title: 'Access A via B'
116   provider: 'module_a'
117 EOF
118     );
119     mkdir($url . '/module_c');
120     file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
121 'access_module_c':
122   title: 'Access C'
123   description: 'bla bla'
124   'restrict access': TRUE
125 EOF
126     );
127     $modules = ['module_a', 'module_b', 'module_c'];
128     $extensions = [
129       'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
130       'module_b' => $this->mockModuleExtension('module_b', 'Module b'),
131       'module_c' => $this->mockModuleExtension('module_c', 'Module c'),
132     ];
133     $this->moduleHandler->expects($this->any())
134       ->method('getImplementations')
135       ->with('permission')
136       ->willReturn([]);
137
138     $this->moduleHandler->expects($this->any())
139       ->method('getModuleList')
140       ->willReturn(array_flip($modules));
141
142     $this->controllerResolver->expects($this->never())
143       ->method('getControllerFromDefinition');
144
145     $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
146
147     // Setup system_rebuild_module_data().
148     $this->permissionHandler->setSystemRebuildModuleData($extensions);
149
150     $actual_permissions = $this->permissionHandler->getPermissions();
151     $this->assertPermissions($actual_permissions);
152
153     $this->assertTrue($this->permissionHandler->moduleProvidesPermissions('module_a'));
154     $this->assertTrue($this->permissionHandler->moduleProvidesPermissions('module_b'));
155     $this->assertTrue($this->permissionHandler->moduleProvidesPermissions('module_c'));
156     $this->assertFalse($this->permissionHandler->moduleProvidesPermissions('module_d'));
157   }
158
159   /**
160    * Tests permissions sort inside a module.
161    *
162    * @covers ::__construct
163    * @covers ::getPermissions
164    * @covers ::buildPermissionsYaml
165    * @covers ::sortPermissions
166    */
167   public function testBuildPermissionsSortPerModule() {
168     vfsStreamWrapper::register();
169     $root = new vfsStreamDirectory('modules');
170     vfsStreamWrapper::setRoot($root);
171
172     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
173     $this->moduleHandler->expects($this->once())
174       ->method('getModuleDirectories')
175       ->willReturn([
176         'module_a' => vfsStream::url('modules/module_a'),
177         'module_b' => vfsStream::url('modules/module_b'),
178         'module_c' => vfsStream::url('modules/module_c'),
179       ]);
180     $this->moduleHandler->expects($this->exactly(3))
181       ->method('getName')
182       ->will($this->returnValueMap([
183         ['module_a', 'Module a'],
184         ['module_b', 'Module b'],
185         ['module_c', 'A Module'],
186       ]));
187
188     $url = vfsStream::url('modules');
189     mkdir($url . '/module_a');
190     file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
191 access_module_a2: single_description2
192 access_module_a1: single_description1
193 EOF
194     );
195     mkdir($url . '/module_b');
196     file_put_contents($url . '/module_b/module_b.permissions.yml',
197       "access_module_a3: single_description"
198     );
199     mkdir($url . '/module_c');
200     file_put_contents($url . '/module_c/module_c.permissions.yml',
201       "access_module_a4: single_description"
202     );
203
204     $modules = ['module_a', 'module_b', 'module_c'];
205     $this->moduleHandler->expects($this->once())
206       ->method('getModuleList')
207       ->willReturn(array_flip($modules));
208
209     $permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
210     $actual_permissions = $permissionHandler->getPermissions();
211     $this->assertEquals(['access_module_a4', 'access_module_a1', 'access_module_a2', 'access_module_a3'],
212       array_keys($actual_permissions));
213   }
214
215   /**
216    * Tests dynamic callback permissions provided by YML files.
217    *
218    * @covers ::__construct
219    * @covers ::getPermissions
220    * @covers ::buildPermissionsYaml
221    */
222   public function testBuildPermissionsYamlCallback() {
223     vfsStreamWrapper::register();
224     $root = new vfsStreamDirectory('modules');
225     vfsStreamWrapper::setRoot($root);
226
227     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
228     $this->moduleHandler->expects($this->once())
229       ->method('getModuleDirectories')
230       ->willReturn([
231         'module_a' => vfsStream::url('modules/module_a'),
232         'module_b' => vfsStream::url('modules/module_b'),
233         'module_c' => vfsStream::url('modules/module_c'),
234       ]);
235
236     $url = vfsStream::url('modules');
237     mkdir($url . '/module_a');
238     file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
239 permission_callbacks:
240   - 'Drupal\\user\\Tests\\TestPermissionCallbacks::singleDescription'
241 EOF
242     );
243     mkdir($url . '/module_b');
244     file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
245 permission_callbacks:
246   - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
247   - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider'
248 EOF
249     );
250     mkdir($url . '/module_c');
251     file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
252 permission_callbacks:
253   - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess'
254 EOF
255     );
256
257     $modules = ['module_a', 'module_b', 'module_c'];
258     $extensions = [
259       'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
260       'module_b' => $this->mockModuleExtension('module_b', 'Module b'),
261       'module_c' => $this->mockModuleExtension('module_c', 'Module c'),
262     ];
263
264     $this->moduleHandler->expects($this->any())
265       ->method('getImplementations')
266       ->with('permission')
267       ->willReturn([]);
268
269     $this->moduleHandler->expects($this->any())
270       ->method('getModuleList')
271       ->willReturn(array_flip($modules));
272
273     $this->controllerResolver->expects($this->at(0))
274       ->method('getControllerFromDefinition')
275       ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::singleDescription')
276       ->willReturn([new TestPermissionCallbacks(), 'singleDescription']);
277     $this->controllerResolver->expects($this->at(1))
278       ->method('getControllerFromDefinition')
279       ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription')
280       ->willReturn([new TestPermissionCallbacks(), 'titleDescription']);
281     $this->controllerResolver->expects($this->at(2))
282       ->method('getControllerFromDefinition')
283       ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider')
284       ->willReturn([new TestPermissionCallbacks(), 'titleProvider']);
285     $this->controllerResolver->expects($this->at(3))
286       ->method('getControllerFromDefinition')
287       ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess')
288       ->willReturn([new TestPermissionCallbacks(), 'titleDescriptionRestrictAccess']);
289
290     $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
291
292     // Setup system_rebuild_module_data().
293     $this->permissionHandler->setSystemRebuildModuleData($extensions);
294
295     $actual_permissions = $this->permissionHandler->getPermissions();
296     $this->assertPermissions($actual_permissions);
297   }
298
299   /**
300    * Tests a YAML file containing both static permissions and a callback.
301    */
302   public function testPermissionsYamlStaticAndCallback() {
303     vfsStreamWrapper::register();
304     $root = new vfsStreamDirectory('modules');
305     vfsStreamWrapper::setRoot($root);
306
307     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
308     $this->moduleHandler->expects($this->once())
309       ->method('getModuleDirectories')
310       ->willReturn([
311         'module_a' => vfsStream::url('modules/module_a'),
312       ]);
313
314     $url = vfsStream::url('modules');
315     mkdir($url . '/module_a');
316     file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
317 'access module a':
318   title: 'Access A'
319   description: 'bla bla'
320 permission_callbacks:
321   - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
322 EOF
323     );
324
325     $modules = ['module_a'];
326     $extensions = [
327       'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
328     ];
329
330     $this->moduleHandler->expects($this->any())
331       ->method('getImplementations')
332       ->with('permission')
333       ->willReturn([]);
334
335     $this->moduleHandler->expects($this->any())
336       ->method('getModuleList')
337       ->willReturn(array_flip($modules));
338
339     $this->controllerResolver->expects($this->once())
340       ->method('getControllerFromDefinition')
341       ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription')
342       ->willReturn([new TestPermissionCallbacks(), 'titleDescription']);
343
344     $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
345
346     // Setup system_rebuild_module_data().
347     $this->permissionHandler->setSystemRebuildModuleData($extensions);
348
349     $actual_permissions = $this->permissionHandler->getPermissions();
350
351     $this->assertCount(2, $actual_permissions);
352     $this->assertEquals($actual_permissions['access module a']['title'], 'Access A');
353     $this->assertEquals($actual_permissions['access module a']['provider'], 'module_a');
354     $this->assertEquals($actual_permissions['access module a']['description'], 'bla bla');
355     $this->assertEquals($actual_permissions['access module b']['title'], 'Access B');
356     $this->assertEquals($actual_permissions['access module b']['provider'], 'module_a');
357     $this->assertEquals($actual_permissions['access module b']['description'], 'bla bla');
358   }
359
360   /**
361    * Checks that the permissions are like expected.
362    *
363    * @param array $actual_permissions
364    *   The actual permissions
365    */
366   protected function assertPermissions(array $actual_permissions) {
367     $this->assertCount(4, $actual_permissions);
368     $this->assertEquals($actual_permissions['access_module_a']['title'], 'single_description');
369     $this->assertEquals($actual_permissions['access_module_a']['provider'], 'module_a');
370     $this->assertEquals($actual_permissions['access module b']['title'], 'Access B');
371     $this->assertEquals($actual_permissions['access module b']['provider'], 'module_b');
372     $this->assertEquals($actual_permissions['access_module_c']['title'], 'Access C');
373     $this->assertEquals($actual_permissions['access_module_c']['provider'], 'module_c');
374     $this->assertEquals($actual_permissions['access_module_c']['restrict access'], TRUE);
375     $this->assertEquals($actual_permissions['access module a via module b']['provider'], 'module_a');
376   }
377
378 }
379
380 class TestPermissionHandler extends PermissionHandler {
381
382   /**
383    * Test module data.
384    *
385    * @var array
386    */
387   protected $systemModuleData;
388
389   protected function systemRebuildModuleData() {
390     return $this->systemModuleData;
391   }
392
393   public function setSystemRebuildModuleData(array $extensions) {
394     $this->systemModuleData = $extensions;
395   }
396
397 }
398
399 class TestPermissionCallbacks {
400
401   public function singleDescription() {
402     return [
403       'access_module_a' => 'single_description',
404     ];
405   }
406
407   public function titleDescription() {
408     return [
409       'access module b' => [
410         'title' => 'Access B',
411         'description' => 'bla bla',
412       ],
413     ];
414   }
415
416   public function titleDescriptionRestrictAccess() {
417     return [
418       'access_module_c' => [
419         'title' => 'Access C',
420         'description' => 'bla bla',
421         'restrict access' => TRUE,
422       ],
423     ];
424   }
425
426   public function titleProvider() {
427     return [
428       'access module a via module b' => [
429         'title' => 'Access A via B',
430         'provider' => 'module_a',
431       ],
432     ];
433   }
434
435 }
436
437 /**
438  * Implements a translation manager in tests.
439  */
440 class TestTranslationManager implements TranslationInterface {
441
442   /**
443    * {@inheritdoc}
444    */
445   public function translate($string, array $args = [], array $options = []) {
446     return new TranslatableMarkup($string, $args, $options, $this);
447   }
448
449   /**
450    * {@inheritdoc}
451    */
452   public function translateString(TranslatableMarkup $translated_string) {
453     return $translated_string->getUntranslatedString();
454   }
455
456   /**
457    * {@inheritdoc}
458    */
459   public function formatPlural($count, $singular, $plural, array $args = [], array $options = []) {
460     return new PluralTranslatableMarkup($count, $singular, $plural, $args, $options, $this);
461   }
462
463 }