PermissionHandlerTest.php

Same filename and directory in other branches
  1. 10 core/modules/user/tests/src/Unit/PermissionHandlerTest.php
  2. 9 core/modules/user/tests/src/Unit/PermissionHandlerTest.php
  3. 8.9.x core/modules/user/tests/src/Unit/PermissionHandlerTest.php
  4. main core/modules/user/tests/src/Unit/PermissionHandlerTest.php

Namespace

Drupal\Tests\user\Unit

File

core/modules/user/tests/src/Unit/PermissionHandlerTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\user\Unit;

use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Tests\UnitTestCase;
use Drupal\user\PermissionHandler;
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamDirectory;
use org\bovigo\vfs\vfsStreamWrapper;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

/**
 * Tests the permission handler.
 */
class PermissionHandlerTest extends UnitTestCase {
  
  /**
   * The tested permission handler.
   *
   * @var \Drupal\user\PermissionHandler
   */
  protected $permissionHandler;
  
  /**
   * The mocked module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $moduleHandler;
  
  /**
   * The mocked callable resolver.
   *
   * @var \Drupal\Core\Utility\CallableResolver|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $callableResolver;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $this->callableResolver = $this->createMock('Drupal\\Core\\Utility\\CallableResolver');
  }
  
  /**
   * Provides an extension object for a given module with a human name.
   *
   * @param string $module
   *   The module machine name.
   * @param string $name
   *   The module human name.
   *
   * @return \Drupal\Core\Extension\Extension
   *   The extension object.
   */
  protected function mockModuleExtension($module, $name) {
    $extension = new Extension('vfs:/', $module, "modules/{$module}");
    $extension->info['name'] = $name;
    return $extension;
  }
  
  /**
   * Tests permissions provided by YML files.
   *
   * @legacy-covers ::__construct
   * @legacy-covers ::getPermissions
   * @legacy-covers ::buildPermissionsYaml
   * @legacy-covers ::moduleProvidesPermissions
   */
  public function testBuildPermissionsYaml() : void {
    vfsStreamWrapper::register();
    $root = new vfsStreamDirectory('modules');
    vfsStreamWrapper::setRoot($root);
    $this->moduleHandler = $this->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $this->moduleHandler
      ->expects($this->once())
      ->method('getModuleDirectories')
      ->willReturn([
      'module_a' => vfsStream::url('modules/module_a'),
      'module_b' => vfsStream::url('modules/module_b'),
      'module_c' => vfsStream::url('modules/module_c'),
    ]);
    $url = vfsStream::url('modules');
    mkdir($url . '/module_a');
    file_put_contents($url . '/module_a/module_a.permissions.yml', "access_module_a: single_description");
    mkdir($url . '/module_b');
    file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
    'access module b':
      title: 'Access B'
      description: 'bla bla'
    'access module a via module b':
      title: 'Access A via B'
      provider: 'module_a'
    EOF);
    mkdir($url . '/module_c');
    file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
    'access_module_c':
      title: 'Access C'
      description: 'bla bla'
      'restrict access': TRUE
    EOF);
    $modules = [
      'module_a',
      'module_b',
      'module_c',
    ];
    $this->moduleHandler
      ->method('getModuleList')
      ->willReturn(array_flip($modules));
    $this->callableResolver
      ->expects($this->never())
      ->method('getCallableFromDefinition');
    $module_extension_list = $this->createStub(ModuleExtensionList::class);
    $this->permissionHandler = new PermissionHandler($this->moduleHandler, $this->getStringTranslationStub(), $this->callableResolver, $module_extension_list);
    $actual_permissions = $this->permissionHandler
      ->getPermissions();
    $this->assertPermissions($actual_permissions);
    $this->assertTrue($this->permissionHandler
      ->moduleProvidesPermissions('module_a'));
    $this->assertTrue($this->permissionHandler
      ->moduleProvidesPermissions('module_b'));
    $this->assertTrue($this->permissionHandler
      ->moduleProvidesPermissions('module_c'));
    $this->assertFalse($this->permissionHandler
      ->moduleProvidesPermissions('module_d'));
  }
  
  /**
   * Tests permissions sort inside a module.
   *
   * @legacy-covers ::__construct
   * @legacy-covers ::getPermissions
   * @legacy-covers ::buildPermissionsYaml
   * @legacy-covers ::sortPermissions
   */
  public function testBuildPermissionsSortPerModule() : void {
    $this->callableResolver
      ->expects($this->never())
      ->method('getCallableFromDefinition');
    vfsStreamWrapper::register();
    $root = new vfsStreamDirectory('modules');
    vfsStreamWrapper::setRoot($root);
    $this->moduleHandler = $this->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $this->moduleHandler
      ->expects($this->once())
      ->method('getModuleDirectories')
      ->willReturn([
      'module_a' => vfsStream::url('modules/module_a'),
      'module_b' => vfsStream::url('modules/module_b'),
      'module_c' => vfsStream::url('modules/module_c'),
    ]);
    $module_extension_list = $this->createMock(ModuleExtensionList::class);
    $module_extension_list->expects($this->exactly(3))
      ->method('getName')
      ->willReturnMap([
      [
        'module_a',
        'Module a',
      ],
      [
        'module_b',
        'Module b',
      ],
      [
        'module_c',
        'A Module',
      ],
    ]);
    $url = vfsStream::url('modules');
    mkdir($url . '/module_a');
    file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
    access_module_a2: single_description2
    access_module_a1: single_description1
    EOF);
    mkdir($url . '/module_b');
    file_put_contents($url . '/module_b/module_b.permissions.yml', "access_module_a3: single_description");
    mkdir($url . '/module_c');
    file_put_contents($url . '/module_c/module_c.permissions.yml', "access_module_a4: single_description");
    $modules = [
      'module_a',
      'module_b',
      'module_c',
    ];
    $this->moduleHandler
      ->expects($this->once())
      ->method('getModuleList')
      ->willReturn(array_flip($modules));
    $permissionHandler = new PermissionHandler($this->moduleHandler, $this->getStringTranslationStub(), $this->callableResolver, $module_extension_list);
    $actual_permissions = $permissionHandler->getPermissions();
    $this->assertEquals([
      'access_module_a4',
      'access_module_a1',
      'access_module_a2',
      'access_module_a3',
    ], array_keys($actual_permissions));
  }
  
  /**
   * Tests dynamic callback permissions provided by YML files.
   *
   * @legacy-covers ::__construct
   * @legacy-covers ::getPermissions
   * @legacy-covers ::buildPermissionsYaml
   */
  public function testBuildPermissionsYamlCallback() : void {
    vfsStreamWrapper::register();
    $root = new vfsStreamDirectory('modules');
    vfsStreamWrapper::setRoot($root);
    $this->moduleHandler = $this->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $this->moduleHandler
      ->expects($this->once())
      ->method('getModuleDirectories')
      ->willReturn([
      'module_a' => vfsStream::url('modules/module_a'),
      'module_b' => vfsStream::url('modules/module_b'),
      'module_c' => vfsStream::url('modules/module_c'),
    ]);
    $url = vfsStream::url('modules');
    mkdir($url . '/module_a');
    file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
    permission_callbacks:
      - 'Drupal\\user\\Tests\\TestPermissionCallbacks::singleDescription'
    EOF);
    mkdir($url . '/module_b');
    file_put_contents($url . '/module_b/module_b.permissions.yml', <<<EOF
    permission_callbacks:
      - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
      - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider'
    EOF);
    mkdir($url . '/module_c');
    file_put_contents($url . '/module_c/module_c.permissions.yml', <<<EOF
    permission_callbacks:
      - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess'
    EOF);
    $modules = [
      'module_a',
      'module_b',
      'module_c',
    ];
    $this->moduleHandler
      ->method('getModuleList')
      ->willReturn(array_flip($modules));
    $this->callableResolver
      ->expects($this->exactly(4))
      ->method('getCallableFromDefinition')
      ->willReturnMap([
      [
        'Drupal\\user\\Tests\\TestPermissionCallbacks::singleDescription',
        [
          new TestPermissionCallbacks(),
          'singleDescription',
        ],
      ],
      [
        'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription',
        [
          new TestPermissionCallbacks(),
          'titleDescription',
        ],
      ],
      [
        'Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider',
        [
          new TestPermissionCallbacks(),
          'titleProvider',
        ],
      ],
      [
        'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess',
        [
          new TestPermissionCallbacks(),
          'titleDescriptionRestrictAccess',
        ],
      ],
    ]);
    $module_extension_list = $this->createStub(ModuleExtensionList::class);
    $this->permissionHandler = new PermissionHandler($this->moduleHandler, $this->getStringTranslationStub(), $this->callableResolver, $module_extension_list);
    $actual_permissions = $this->permissionHandler
      ->getPermissions();
    $this->assertPermissions($actual_permissions);
  }
  
  /**
   * Tests a YAML file containing both static permissions and a callback.
   */
  public function testPermissionsYamlStaticAndCallback() : void {
    vfsStreamWrapper::register();
    $root = new vfsStreamDirectory('modules');
    vfsStreamWrapper::setRoot($root);
    $this->moduleHandler = $this->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $this->moduleHandler
      ->expects($this->once())
      ->method('getModuleDirectories')
      ->willReturn([
      'module_a' => vfsStream::url('modules/module_a'),
    ]);
    $url = vfsStream::url('modules');
    mkdir($url . '/module_a');
    file_put_contents($url . '/module_a/module_a.permissions.yml', <<<EOF
    'access module a':
      title: 'Access A'
      description: 'bla bla'
    permission_callbacks:
      - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'
    EOF);
    $modules = [
      'module_a',
    ];
    $this->moduleHandler
      ->method('getModuleList')
      ->willReturn(array_flip($modules));
    $this->callableResolver
      ->expects($this->once())
      ->method('getCallableFromDefinition')
      ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription')
      ->willReturn([
      new TestPermissionCallbacks(),
      'titleDescription',
    ]);
    $module_extension_list = $this->createStub(ModuleExtensionList::class);
    $this->permissionHandler = new PermissionHandler($this->moduleHandler, $this->getStringTranslationStub(), $this->callableResolver, $module_extension_list);
    $actual_permissions = $this->permissionHandler
      ->getPermissions();
    $this->assertCount(2, $actual_permissions);
    $this->assertEquals('Access A', $actual_permissions['access module a']['title']);
    $this->assertEquals('module_a', $actual_permissions['access module a']['provider']);
    $this->assertEquals('bla bla', $actual_permissions['access module a']['description']);
    $this->assertEquals('Access B', $actual_permissions['access module b']['title']);
    $this->assertEquals('module_a', $actual_permissions['access module b']['provider']);
    $this->assertEquals('bla bla', $actual_permissions['access module b']['description']);
  }
  
  /**
   * Checks that the permissions are like expected.
   *
   * @param array $actual_permissions
   *   The actual permissions.
   *
   * @internal
   */
  protected function assertPermissions(array $actual_permissions) : void {
    $this->assertCount(4, $actual_permissions);
    $this->assertEquals('single_description', $actual_permissions['access_module_a']['title']);
    $this->assertEquals('module_a', $actual_permissions['access_module_a']['provider']);
    $this->assertEquals('Access B', $actual_permissions['access module b']['title']);
    $this->assertEquals('module_b', $actual_permissions['access module b']['provider']);
    $this->assertEquals('Access C', $actual_permissions['access_module_c']['title']);
    $this->assertEquals('module_c', $actual_permissions['access_module_c']['provider']);
    $this->assertTrue($actual_permissions['access_module_c']['restrict access']);
    $this->assertEquals('module_a', $actual_permissions['access module a via module b']['provider']);
  }

}

/**
 * Provider for testing permissions callbacks.
 */
class TestPermissionCallbacks {
  
  /**
   * Callback that returns a single description.
   */
  public function singleDescription() {
    return [
      'access_module_a' => 'single_description',
    ];
  }
  
  /**
   * Callback that returns the title and description.
   */
  public function titleDescription() {
    return [
      'access module b' => [
        'title' => 'Access B',
        'description' => 'bla bla',
      ],
    ];
  }
  
  /**
   * Callback that returns restricted access.
   */
  public function titleDescriptionRestrictAccess() {
    return [
      'access_module_c' => [
        'title' => 'Access C',
        'description' => 'bla bla',
        'restrict access' => TRUE,
      ],
    ];
  }
  
  /**
   * Callback that returns the title.
   */
  public function titleProvider() {
    return [
      'access module a via module b' => [
        'title' => 'Access A via B',
        'provider' => 'module_a',
      ],
    ];
  }

}

Classes

Title Deprecated Summary
PermissionHandlerTest Tests the permission handler.
TestPermissionCallbacks Provider for testing permissions callbacks.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.