0

I Have

/**
 * @ContentEntityType(
 *   id = "test_entity",
 *   label = @Translation("label"),
 *   bundle_label = @Translation("Testing entity type"),
 *   handlers = {
 *     bla...
 *     "access" = "Drupal\test_entity\TestEntityAccessControlHandler",
 *     "form" = {
 *       "default" = "Drupal\test_entity\TestEntityForm",
 *       "edit" = "Drupal\test_entity\TestEntityForm",
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm"
 *     },
 *     "route_provider" = { "admin" = "Drupal\test_entity\Entity\TestEntityRouteProvider" },
 *     "list_builder" = "Drupal\test_entity\TestEntityListBuilder",
 *   },
 *   admin_permission = "administer test entity",
 *   translatable = FALSE,
 *   entity_keys = { "id" = "id", "bundle" = "type", "label" = "test", "uuid" = "uuid" },
 *   bundle_entity_type = "test_entity_type",
 *   field_ui_base_route = "entity.test_entity_type.edit_form",
 *   common_reference_target = TRUE,
 *   permission_granularity = "bundle",
 *   links = {
 *     "canonical" = "/admin/testing/my-entity/{test_entity}",
 *     "delete-form" = "/admin/testing/my-entity/{test_entity}/delete",
 *     "edit-form" = "/admin/testing/my-entity/{test_entity}/edit",
 *     "collection" = "/admin/testing/my-entity",
 *   }
 * )
 */

with route

namespace Drupal\test_entity\Entity;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

/**
 * Provides routes for nodes.
 */
class TestEntityRouteProvider extends AdminHtmlRouteProvider {

    /**
     * {@inheritdoc}
     */
    public function getRoutes( EntityTypeInterface $entity_type) {
    $route_collection = new RouteCollection();
    $route = (new Route('/admin/testing/entity/{test_entity}'))
      ->addDefaults([
        '_entity_view' => 'test_entity.full',
        '_title' => 'blah...',
      ])
      ->setRequirement('_entity_access', 'test_entity.view');
    $route_collection->add('entity.test_entity.canonical', $route);

    $route = (new Route('/admin/testing/entity/{test_entity}/delete'))
      ->addDefaults([
        '_entity_form' => 'test_entity.delete',
        '_title' => 'blah del...',
      ])
      ->setRequirement('_entity_access', 'test_entity.delete')
      ->setOption('_test_entity_operation_route', TRUE)
      ->setOption('_admin_route', TRUE);
    $route_collection->add('entity.test_entity.delete_form', $route);

    $route = (new Route('/admin/testing/entity/{test_entity}/edit'))
      ->setDefault('_entity_form', 'test_entity.edit')
      ->setRequirement('_entity_access', 'test_entity.update')
      ->setOption('_test_entity_operation_route', TRUE)
      ->setOption('_admin_route', TRUE);;
    $route_collection->add('entity.test_entity.edit_form', $route);

    return $route_collection;
  }

}

and TestEntityAccessControlHandler

namespace Drupal\test_entity;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
 * Defines the access control handler for the node entity type.
 *
 * @see \Drupal\test_entity\Entity\TestEntity
 * @ingroup test_entity_access
 */
class TestEntityAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {

  /**
   * The entity manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityManager;

  /**
   * Constructs a TestEntityAccessControlHandler object.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   */
  public function __construct(EntityTypeInterface $entity_type, EntityTypeManagerInterface $entity_manager) {
    parent::__construct($entity_type);
    $this->entityManager = $entity_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    return new static(
      $entity_type,
      $container->get('entity.manager')
    );
  }


  /**
   * {@inheritdoc}
   */
  public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
    $account = $this->prepareUser($account);
    $result = parent::access($entity, $operation, $account, TRUE);
    if ($result->isForbidden()) {
      return $return_as_object ? $result : $result->isAllowed();
    }
    if ($operation !== 'view') {
      $bundle = $this->entityManager->getStorage($this->entityType->getBundleEntityType())->load($entity->bundle());
      if ($bundle->isReadOnly()) {
        $result = AccessResult::forbidden()->cachePerPermissions();
      }
    }
    if ($result->isForbidden()) {
      return $return_as_object ? $result : $result->isAllowed();
    }
    $permission = $operation === 'update' ? "edit {$entity->bundle()} test entity" : "{$operation} {$entity->bundle()} test entity";
    $result = AccessResult::allowedIfHasPermission($account, $permission);
    return $return_as_object ? $result : $result->isAllowed();
  }

  /**
   * {@inheritdoc}
   */
  public function createAccess($entity_bundle = NULL, AccountInterface $account = NULL, array $context = array(), $return_as_object = FALSE) {
    $account = $this->prepareUser($account);
    $result = parent::createAccess($entity_bundle, $account, $context, TRUE)->cachePerPermissions();
    if ($result->isForbidden()) {
      return $return_as_object ? $result : $result->isAllowed();
    }
    $bundle = $this->entityManager->getStorage($this->entityType->getBundleEntityType())->load($entity_bundle);
    if ($bundle->isReadOnly()) {
      $result = AccessResult::forbidden()->cachePerPermissions();
    }
    if ($result->isForbidden()) {
      return $return_as_object ? $result : $result->isAllowed();
    }
    $permission = "create $entity_bundle test entity";

    $result = AccessResult::allowedIfHasPermission($account, $permission);
    return $return_as_object ? $result : $result->isAllowed();
  }

  /**
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    $result = parent::checkAccess($entity, $operation, $account);
    if ($result->isForbidden()) {
      return $result;
    }
    if ($operation !== 'view') {
      $bundle = $this->entityManager->getStorage($this->entityType->getBundleEntityType())->load($entity->bundle());
      if ($bundle->isReadOnly()) {
        $result = AccessResult::forbidden()->cachePerPermissions();
      }
    }
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
    $result = parent::checkCreateAccess($account, $context, $entity_bundle);
    if ($result->isForbidden()) {
      return $result;
    }
    if (!is_null($entity_bundle)) {
      return AccessResult::neutral()->cachePerPermissions();
    }
    $bundle = $this->entityManager->getStorage($this->entityType->getBundleEntityType())->load($entity_bundle);
    if ($bundle->isReadOnly()) {
      $result = AccessResult::forbidden()->cachePerPermissions();
    }
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
    $result = parent::checkFieldAccess($operation, $field_definition, $account, $items);
    if (!$result) {
      return $result;
    }
    if ($operation !== 'view') {
      $bundle_id = $field_definition->getTargetBundle();
      if ($bundle_id) {
        $bundle = $this->entityManager->getStorage($this->entityType->getBundleEntityType())->load($bundle_id);
        if ($bundle->isReadOnly()) {
          $result = FALSE;
        }
      }
    }
    return $result;
  }
}

service.yml:

   access_check.test_entity.add:
      class: Drupal\test_entity\Access\TestEntityAddAccessCheck
      arguments: ['@entity.manager']
      tags:
        - { name: access_check, applies_to: _test_entity_add_access }

with TestEntityAddAccessCheck:

namespace Drupal\test_entity\Access;

use bla

/**
 * Determines access to for entity add pages.
 *
 * @ingroup test_entity_access
 */
class TestEntityAddAccessCheck implements AccessInterface {

  protected $entityManager;

  public function __construct(EntityManagerInterface $entity_manager) {
    $this->entityManager = $entity_manager;
  }


  public function access(AccountInterface $account, ExternalEntityTypeInterface $external_entity_type = NULL) {
    $access_control_handler = $this->entityManager->getAccessControlHandler('test_entity');
    // Checking whether a external entity of a particular type may be created.
    if ($external_entity_type) {
      return $access_control_handler->createAccess($external_entity_type->id(), $account, [], TRUE);
    }

    $types = $this->entityManager->getStorage('test_entity_type')->loadMultiple();
    foreach ($types as $node_type) {
      if (($access = $access_control_handler->createAccess($node_type->id(), $account, [], TRUE)) && $access->isAllowed()) {
        return $access;
      }
    }
    if ($types && $account->hasPermission('administer test entity types')) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    if (!$types) {
      return AccessResult::forbidden();
    }

    // No opinion.
    return AccessResult::neutral();
  }

}

i do some deep trace and here is what i found:

in EntityAccessCheck:Access i found out that my $route_match->parameters->parameters[test_entity] = 0

which if i compare with node or other entity type it has the entity object there, then it will lead to:

when it reach this piece of code:

if ($parameters->has($entity_type)) {
  $entity = $parameters->get($entity_type); // THIS $entity = 0
  if ($entity instanceof EntityInterface) { // which causing this statement FALSE
    return $entity->access($operation, $account, TRUE);
  }
}

it didn't go into that second if statement therefore the method will return: return AccessResult::neutral();

that causing no permission.

the problem is: whenever i access:

"/admin/testing/my-entity/{test_entity}",

"/admin/testing/my-entity/{test_entity}/delete",

"/admin/testing/my-entity/{test_entity}/edit",

"/admin/testing/my-entity",

they're all giving me

You are not authorized to access this page.

what's possibly went wrong here?

it's been a while i tried to crack this,,,

  • You don't seem to have an access control handler nor an admin permission, then that's exactly what happens? – Berdir May 4 '17 at 19:34
  • Thanks @berdir, is there any reference or sample on how to implement access control handler and/or admin permission that i can take a look? – AnD May 5 '17 at 5:44
  • Basically every entity type in core is an example. Node is the most complex one, Term is a good example, with TermAccessControlHandler – Berdir May 5 '17 at 18:26
  • I have access control handler (see my updated question) but still it giving me access denied, do i need to have admin permission? how to add admin permission? – AnD May 6 '17 at 3:16
  • i do some deep trace and here is what i found: in EntityAccessCheck:Access i found out that my $route_match->parameters->parameters[test_entity] = 0 while the other core entity valued it with its entity object.. and this causing my route forbidden but i dont have any idea why that happen. detailed explanation in my question – AnD May 6 '17 at 3:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.