You are here

class UserAccessControlHandlerTest in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php \Drupal\Tests\user\Unit\UserAccessControlHandlerTest

Tests the user access controller.

@group Drupal @group User

@coversDefaultClass \Drupal\user\UserAccessControlHandler

Hierarchy

Expanded class hierarchy of UserAccessControlHandlerTest

File

core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php, line 19

Namespace

Drupal\Tests\user\Unit
View source
class UserAccessControlHandlerTest extends UnitTestCase {

  /**
   * The user access controller to test.
   *
   * @var \Drupal\user\UserAccessControlHandler
   */
  protected $accessControlHandler;

  /**
   * The mock user account with view access.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $viewer;

  /**
   * The mock user account with 'view user email addresses' permission.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $emailViewer;

  /**
   * The mock user account that is able to change their own account name.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $owner;

  /**
   * The mock administrative test user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $admin;

  /**
   * The mocked test field items.
   *
   * @var \Drupal\Core\Field\FieldItemList
   */
  protected $items;

  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    $cache_contexts_manager = $this
      ->prophesize(CacheContextsManager::class);
    $cache_contexts_manager
      ->assertValidTokens()
      ->willReturn(TRUE);
    $cache_contexts_manager
      ->reveal();
    $container = new Container();
    $container
      ->set('cache_contexts_manager', $cache_contexts_manager);
    \Drupal::setContainer($container);
    $this->viewer = $this
      ->createMock('\\Drupal\\Core\\Session\\AccountInterface');
    $this->viewer
      ->expects($this
      ->any())
      ->method('hasPermission')
      ->will($this
      ->returnValue(FALSE));
    $this->viewer
      ->expects($this
      ->any())
      ->method('id')
      ->will($this
      ->returnValue(1));
    $this->owner = $this
      ->createMock('\\Drupal\\Core\\Session\\AccountInterface');
    $this->owner
      ->expects($this
      ->any())
      ->method('hasPermission')
      ->willReturnMap([
      [
        'administer users',
        FALSE,
      ],
      [
        'change own username',
        TRUE,
      ],
    ]);
    $this->owner
      ->expects($this
      ->any())
      ->method('id')
      ->will($this
      ->returnValue(2));
    $this->admin = $this
      ->createMock('\\Drupal\\Core\\Session\\AccountInterface');
    $this->admin
      ->expects($this
      ->any())
      ->method('hasPermission')
      ->will($this
      ->returnValue(TRUE));
    $this->emailViewer = $this
      ->createMock('\\Drupal\\Core\\Session\\AccountInterface');
    $this->emailViewer
      ->expects($this
      ->any())
      ->method('hasPermission')
      ->willReturnMap([
      [
        'view user email addresses',
        TRUE,
      ],
    ]);
    $this->emailViewer
      ->expects($this
      ->any())
      ->method('id')
      ->will($this
      ->returnValue(3));
    $entity_type = $this
      ->createMock('Drupal\\Core\\Entity\\EntityTypeInterface');
    $this->accessControlHandler = new UserAccessControlHandler($entity_type);
    $module_handler = $this
      ->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $module_handler
      ->expects($this
      ->any())
      ->method('getImplementations')
      ->will($this
      ->returnValue([]));
    $this->accessControlHandler
      ->setModuleHandler($module_handler);
    $this->items = $this
      ->getMockBuilder('Drupal\\Core\\Field\\FieldItemList')
      ->disableOriginalConstructor()
      ->getMock();
    $this->items
      ->expects($this
      ->any())
      ->method('defaultAccess')
      ->will($this
      ->returnValue(AccessResult::allowed()));
  }

  /**
   * Asserts correct field access grants for a field.
   */
  public function assertFieldAccess($field, $viewer, $target, $view, $edit) {
    $field_definition = $this
      ->createMock('Drupal\\Core\\Field\\FieldDefinitionInterface');
    $field_definition
      ->expects($this
      ->any())
      ->method('getName')
      ->will($this
      ->returnValue($field));
    $this->items
      ->expects($this
      ->any())
      ->method('getEntity')
      ->will($this
      ->returnValue($this->{$target}));
    foreach ([
      'view' => $view,
      'edit' => $edit,
    ] as $operation => $result) {
      $result_text = !isset($result) ? 'null' : ($result ? 'true' : 'false');
      $message = "User '{$field}' field access returns '{$result_text}' with operation '{$operation}' for '{$viewer}' accessing '{$target}'";
      $this
        ->assertSame($result, $this->accessControlHandler
        ->fieldAccess($operation, $field_definition, $this->{$viewer}, $this->items), $message);
    }
  }

  /**
   * Ensures user name access is working properly.
   *
   * @dataProvider userNameProvider
   */
  public function testUserNameAccess($viewer, $target, $view, $edit) {
    $this
      ->assertFieldAccess('name', $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for testUserNameAccess().
   */
  public function userNameProvider() {
    $name_access = [
      // The viewer user is allowed to see user names on all accounts.
      [
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => FALSE,
      ],
      [
        'viewer' => 'owner',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => FALSE,
      ],
      [
        'viewer' => 'viewer',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => FALSE,
      ],
      // The owner user is allowed to change its own user name.
      [
        'viewer' => 'owner',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ],
      // The users-administrator user has full access.
      [
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ],
    ];
    return $name_access;
  }

  /**
   * Tests that private user settings cannot be viewed by other users.
   *
   * @dataProvider hiddenUserSettingsProvider
   */
  public function testHiddenUserSettings($field, $viewer, $target, $view, $edit) {
    $this
      ->assertFieldAccess($field, $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for testHiddenUserSettings().
   */
  public function hiddenUserSettingsProvider() {
    $access_info = [];
    $fields = [
      'preferred_langcode',
      'preferred_admin_langcode',
      'timezone',
      'mail',
    ];
    foreach ($fields as $field) {
      $access_info[] = [
        'field' => $field,
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => TRUE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'viewer',
        'target' => 'owner',
        'view' => FALSE,
        // Anyone with edit access to the user can also edit these fields. In
        // reality edit access will already be checked on entity level and the
        // user without view access will typically not be able to edit.
        'edit' => TRUE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'owner',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'emailViewer',
        'target' => 'owner',
        'view' => $field === 'mail',
        // See note above.
        'edit' => TRUE,
      ];
    }
    return $access_info;
  }

  /**
   * Tests that private user settings cannot be viewed by other users.
   *
   * @dataProvider adminFieldAccessProvider
   */
  public function testAdminFieldAccess($field, $viewer, $target, $view, $edit) {
    $this
      ->assertFieldAccess($field, $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for testAdminFieldAccess().
   */
  public function adminFieldAccessProvider() {
    $access_info = [];
    $fields = [
      'roles',
      'status',
      'access',
      'login',
      'init',
    ];
    foreach ($fields as $field) {
      $access_info[] = [
        'field' => $field,
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => FALSE,
        'edit' => FALSE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'viewer',
        'target' => 'owner',
        'view' => FALSE,
        'edit' => FALSE,
      ];
      $access_info[] = [
        'field' => $field,
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ];
    }
    return $access_info;
  }

  /**
   * Tests that passwords cannot be viewed, just edited.
   *
   * @dataProvider passwordAccessProvider
   */
  public function testPasswordAccess($viewer, $target, $view, $edit) {
    $this
      ->assertFieldAccess('pass', $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for passwordAccessProvider().
   */
  public function passwordAccessProvider() {
    $pass_access = [
      [
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => FALSE,
        'edit' => TRUE,
      ],
      [
        'viewer' => 'viewer',
        'target' => 'owner',
        'view' => FALSE,
        // Anyone with edit access to the user can also edit these fields. In
        // reality edit access will already be checked on entity level and the
        // user without view access will typically not be able to edit.
        'edit' => TRUE,
      ],
      [
        'viewer' => 'owner',
        'target' => 'viewer',
        'view' => FALSE,
        'edit' => TRUE,
      ],
      [
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => FALSE,
        'edit' => TRUE,
      ],
    ];
    return $pass_access;
  }

  /**
   * Tests the user created field access.
   *
   * @dataProvider createdAccessProvider
   */
  public function testCreatedAccess($viewer, $target, $view, $edit) {
    $this
      ->assertFieldAccess('created', $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for testCreatedAccess().
   */
  public function createdAccessProvider() {
    $created_access = [
      [
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => FALSE,
      ],
      [
        'viewer' => 'owner',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => FALSE,
      ],
      [
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ],
    ];
    return $created_access;
  }

  /**
   * Tests access to a non-existing base field.
   *
   * @dataProvider NonExistingFieldAccessProvider
   */
  public function testNonExistingFieldAccess($viewer, $target, $view, $edit) {

    // By default everyone has access to all fields that do not have explicit
    // access control.
    // @see EntityAccessControlHandler::checkFieldAccess()
    $this
      ->assertFieldAccess('some_non_existing_field', $viewer, $target, $view, $edit);
  }

  /**
   * Provides test data for testNonExistingFieldAccess().
   */
  public function NonExistingFieldAccessProvider() {
    $created_access = [
      [
        'viewer' => 'viewer',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => TRUE,
      ],
      [
        'viewer' => 'owner',
        'target' => 'viewer',
        'view' => TRUE,
        'edit' => TRUE,
      ],
      [
        'viewer' => 'admin',
        'target' => 'owner',
        'view' => TRUE,
        'edit' => TRUE,
      ],
    ];
    return $created_access;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpUnitWarnings::$deprecationWarnings private static property Deprecation warnings from PHPUnit to raise with @trigger_error().
PhpUnitWarnings::addWarning public function Converts PHPUnit deprecation warnings to E_USER_DEPRECATED.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals Deprecated protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.
UnitTestCase::setUpBeforeClass public static function
UserAccessControlHandlerTest::$accessControlHandler protected property The user access controller to test.
UserAccessControlHandlerTest::$admin protected property The mock administrative test user.
UserAccessControlHandlerTest::$emailViewer protected property The mock user account with 'view user email addresses' permission.
UserAccessControlHandlerTest::$items protected property The mocked test field items.
UserAccessControlHandlerTest::$owner protected property The mock user account that is able to change their own account name.
UserAccessControlHandlerTest::$viewer protected property The mock user account with view access.
UserAccessControlHandlerTest::adminFieldAccessProvider public function Provides test data for testAdminFieldAccess().
UserAccessControlHandlerTest::assertFieldAccess public function Asserts correct field access grants for a field.
UserAccessControlHandlerTest::createdAccessProvider public function Provides test data for testCreatedAccess().
UserAccessControlHandlerTest::hiddenUserSettingsProvider public function Provides test data for testHiddenUserSettings().
UserAccessControlHandlerTest::NonExistingFieldAccessProvider public function Provides test data for testNonExistingFieldAccess().
UserAccessControlHandlerTest::passwordAccessProvider public function Provides test data for passwordAccessProvider().
UserAccessControlHandlerTest::setUp protected function Overrides UnitTestCase::setUp
UserAccessControlHandlerTest::testAdminFieldAccess public function Tests that private user settings cannot be viewed by other users.
UserAccessControlHandlerTest::testCreatedAccess public function Tests the user created field access.
UserAccessControlHandlerTest::testHiddenUserSettings public function Tests that private user settings cannot be viewed by other users.
UserAccessControlHandlerTest::testNonExistingFieldAccess public function Tests access to a non-existing base field.
UserAccessControlHandlerTest::testPasswordAccess public function Tests that passwords cannot be viewed, just edited.
UserAccessControlHandlerTest::testUserNameAccess public function Ensures user name access is working properly.
UserAccessControlHandlerTest::userNameProvider public function Provides test data for testUserNameAccess().