5 * Contains \Drupal\Tests\user\Unit\PermissionHandlerTest.
8 namespace Drupal\Tests\user\Unit;
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;
21 * Tests the permission handler.
25 * @coversDefaultClass \Drupal\user\PermissionHandler
27 class PermissionHandlerTest extends UnitTestCase {
30 * The tested permission handler.
32 * @var \Drupal\Tests\user\Unit\TestPermissionHandler|\Drupal\user\PermissionHandler
34 protected $permissionHandler;
37 * The mocked module handler.
39 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
41 protected $moduleHandler;
44 * The mocked string translation.
46 * @var \Drupal\Tests\user\Unit\TestTranslationManager
48 protected $stringTranslation;
51 * The mocked controller resolver.
53 * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
55 protected $controllerResolver;
60 protected function setUp() {
63 $this->stringTranslation = new TestTranslationManager();
64 $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
68 * Provides an extension object for a given module with a human name.
70 * @param string $module
71 * The module machine name.
73 * The module human name.
75 * @return \Drupal\Core\Extension\Extension
76 * The extension object.
78 protected function mockModuleExtension($module, $name) {
79 $extension = new Extension($this->root, $module, "modules/$module");
80 $extension->info['name'] = $name;
85 * Tests permissions provided by YML files.
87 * @covers ::__construct
88 * @covers ::getPermissions
89 * @covers ::buildPermissionsYaml
90 * @covers ::moduleProvidesPermissions
92 public function testBuildPermissionsYaml() {
93 vfsStreamWrapper::register();
94 $root = new vfsStreamDirectory('modules');
95 vfsStreamWrapper::setRoot($root);
97 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
98 $this->moduleHandler->expects($this->once())
99 ->method('getModuleDirectories')
101 'module_a' => vfsStream::url('modules/module_a'),
102 'module_b' => vfsStream::url('modules/module_b'),
103 'module_c' => vfsStream::url('modules/module_c'),
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
113 description: 'bla bla'
114 'access module a via module b':
115 title: 'Access A via B'
119 mkdir($url . '/module_c');
120 file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
123 description: 'bla bla'
124 'restrict access': TRUE
127 $modules = ['module_a', 'module_b', 'module_c'];
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'),
133 $this->moduleHandler->expects($this->any())
134 ->method('getImplementations')
138 $this->moduleHandler->expects($this->any())
139 ->method('getModuleList')
140 ->willReturn(array_flip($modules));
142 $this->controllerResolver->expects($this->never())
143 ->method('getControllerFromDefinition');
145 $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
147 // Setup system_rebuild_module_data().
148 $this->permissionHandler->setSystemRebuildModuleData($extensions);
150 $actual_permissions = $this->permissionHandler->getPermissions();
151 $this->assertPermissions($actual_permissions);
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'));
160 * Tests permissions sort inside a module.
162 * @covers ::__construct
163 * @covers ::getPermissions
164 * @covers ::buildPermissionsYaml
165 * @covers ::sortPermissions
167 public function testBuildPermissionsSortPerModule() {
168 vfsStreamWrapper::register();
169 $root = new vfsStreamDirectory('modules');
170 vfsStreamWrapper::setRoot($root);
172 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
173 $this->moduleHandler->expects($this->once())
174 ->method('getModuleDirectories')
176 'module_a' => vfsStream::url('modules/module_a'),
177 'module_b' => vfsStream::url('modules/module_b'),
178 'module_c' => vfsStream::url('modules/module_c'),
180 $this->moduleHandler->expects($this->exactly(3))
182 ->will($this->returnValueMap([
183 ['module_a', 'Module a'],
184 ['module_b', 'Module b'],
185 ['module_c', 'A Module'],
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
195 mkdir($url . '/module_b');
196 file_put_contents($url . '/module_b/module_b.permissions.yml',
197 "access_module_a3: single_description"
199 mkdir($url . '/module_c');
200 file_put_contents($url . '/module_c/module_c.permissions.yml',
201 "access_module_a4: single_description"
204 $modules = ['module_a', 'module_b', 'module_c'];
205 $this->moduleHandler->expects($this->once())
206 ->method('getModuleList')
207 ->willReturn(array_flip($modules));
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));
216 * Tests dynamic callback permissions provided by YML files.
218 * @covers ::__construct
219 * @covers ::getPermissions
220 * @covers ::buildPermissionsYaml
222 public function testBuildPermissionsYamlCallback() {
223 vfsStreamWrapper::register();
224 $root = new vfsStreamDirectory('modules');
225 vfsStreamWrapper::setRoot($root);
227 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
228 $this->moduleHandler->expects($this->once())
229 ->method('getModuleDirectories')
231 'module_a' => vfsStream::url('modules/module_a'),
232 'module_b' => vfsStream::url('modules/module_b'),
233 'module_c' => vfsStream::url('modules/module_c'),
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'
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'
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'
257 $modules = ['module_a', 'module_b', 'module_c'];
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'),
264 $this->moduleHandler->expects($this->any())
265 ->method('getImplementations')
269 $this->moduleHandler->expects($this->any())
270 ->method('getModuleList')
271 ->willReturn(array_flip($modules));
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']);
290 $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
292 // Setup system_rebuild_module_data().
293 $this->permissionHandler->setSystemRebuildModuleData($extensions);
295 $actual_permissions = $this->permissionHandler->getPermissions();
296 $this->assertPermissions($actual_permissions);
300 * Tests a YAML file containing both static permissions and a callback.
302 public function testPermissionsYamlStaticAndCallback() {
303 vfsStreamWrapper::register();
304 $root = new vfsStreamDirectory('modules');
305 vfsStreamWrapper::setRoot($root);
307 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
308 $this->moduleHandler->expects($this->once())
309 ->method('getModuleDirectories')
311 'module_a' => vfsStream::url('modules/module_a'),
314 $url = vfsStream::url('modules');
315 mkdir($url . '/module_a');
316 file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
319 description: 'bla bla'
320 permission_callbacks:
321 - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
325 $modules = ['module_a'];
327 'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
330 $this->moduleHandler->expects($this->any())
331 ->method('getImplementations')
335 $this->moduleHandler->expects($this->any())
336 ->method('getModuleList')
337 ->willReturn(array_flip($modules));
339 $this->controllerResolver->expects($this->once())
340 ->method('getControllerFromDefinition')
341 ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription')
342 ->willReturn([new TestPermissionCallbacks(), 'titleDescription']);
344 $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
346 // Setup system_rebuild_module_data().
347 $this->permissionHandler->setSystemRebuildModuleData($extensions);
349 $actual_permissions = $this->permissionHandler->getPermissions();
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');
361 * Checks that the permissions are like expected.
363 * @param array $actual_permissions
364 * The actual permissions
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');
380 class TestPermissionHandler extends PermissionHandler {
387 protected $systemModuleData;
389 protected function systemRebuildModuleData() {
390 return $this->systemModuleData;
393 public function setSystemRebuildModuleData(array $extensions) {
394 $this->systemModuleData = $extensions;
399 class TestPermissionCallbacks {
401 public function singleDescription() {
403 'access_module_a' => 'single_description',
407 public function titleDescription() {
409 'access module b' => [
410 'title' => 'Access B',
411 'description' => 'bla bla',
416 public function titleDescriptionRestrictAccess() {
418 'access_module_c' => [
419 'title' => 'Access C',
420 'description' => 'bla bla',
421 'restrict access' => TRUE,
426 public function titleProvider() {
428 'access module a via module b' => [
429 'title' => 'Access A via B',
430 'provider' => 'module_a',
438 * Implements a translation manager in tests.
440 class TestTranslationManager implements TranslationInterface {
445 public function translate($string, array $args = [], array $options = []) {
446 return new TranslatableMarkup($string, $args, $options, $this);
452 public function translateString(TranslatableMarkup $translated_string) {
453 return $translated_string->getUntranslatedString();
459 public function formatPlural($count, $singular, $plural, array $args = [], array $options = []) {
460 return new PluralTranslatableMarkup($count, $singular, $plural, $args, $options, $this);