Problem/Motivation

\Drupal\field_ui\Access\ViewModeAccessCheck and \Drupal\field_ui\Access\FormModeAccessCheck are bundled inside the field_ui.module.

Pre-REST, this made sense because there was only one way to handle view modes and that was via the field_ui.module.

But this adds a dependency on field_ui.module if you want to get entity_view_mode and entity_form_mode configuration via REST API.

See related issues: #2843780: EntityResource: Provide comprehensive test coverage for EntityFormMode entity and #2843781: EntityResource: Provide comprehensive test coverage for EntityViewMode entity.

Proposed resolution

Move \Drupal\field_ui\Access\ViewModeAccessCheck and \Drupal\field_ui\Access\FormModeAccessCheck from field_ui.module into entity access handlers inside the Entity core component.

Note: Filing this against the rest.module component for now to get feedback from the REST maintainers first.

Remaining tasks

  1. Write patch.
  2. Write tests.

User interface changes

API changes

Data model changes

CommentFileSizeAuthor
#12 2867266-12.patch1.04 KBwim leers
#11 2867266-11.patch4.67 KBAnonymous (not verified)

Comments

arshadcn created an issue. See original summary.

shadcn’s picture

shadcn’s picture

wim leers’s picture

Component: rest.module » field_ui.module
jamesdeee’s picture

Assigned: Unassigned » jamesdeee

I'm going to assign this to myself and have a stab at it. I hope everyone's happy with that - please let me know if not. I've been helping to work through some of the issues at Write EntityResourceTestBase subclasses for every other entity type, and I was just about to have a go at test coverage for EntityFormMode when I saw that @vaplas has linked this ticket.

jamesdeee’s picture

I'm trying to step my way through this. The first part of the requirement is reasonably clear - ViewModeAccessCheck and FormModeAccessCheck both have an access method (in fact, bar the constructor, that's the only method they have).

The bit I'm less clear on is where they need to go. I've stepped through the code and it looks like entity access is governed by EntityAccessCheck and EntityAccessControlHandler.

They both have their own access methods. I'm sort of assuming one of them gets called by the access method on Entity itself - though I can't quite see how.

So is the task here to adapt one of access methods that already exist in the Entity code, so that the method recognises when the call is for an entity_view_form or an entity_view_mode, and then applies the logic in the access checker classes in field_ui? And if so, can anyone help me out with which access method needs to be adapted?

Or am I just waaay off the mark?

berdir’s picture

I guess that we need is to convert those into subclasses of EntityAccessControlHandler, similar to other entity types and then put it into the annotation of those entity classes. possibly even the same class/code if it's generic enough. And then update \Drupal\field_ui\Routing\RouteSubscriber::alterRoutes() to use them.

But I'm not sure if we can really replace that, as view/form displays are special in that they are auto-created, at least the default display. So that might not actually exist, but we still want to allow access.

So likely we can't actually replace them completely, just model the new code based on them.

jamesdeee’s picture

So you'd end up with something like below (obviously this wouldn't work, as the variables in the function signature don't match the variables in the function body, as I've just cut and pasted this together).

I've put this in the core/config/Entity directory. Is that what you have in mind?

I assume there would need to be some logic somewhere that would match the entity type to the AccessControlHandler - but I don't really understand where that would happen.

<?php

namespace Drupal\Core\Config;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Defines the access control handler for the comment entity type.
 *
 * @see \Drupal\comment\Entity\Comment
 */
class EntityViewModeAcccessControlHandler extends EntityAccessControlHandler {

  /**
   * {@inheritdoc}
   */
  public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE) {

    $access = AccessResult::neutral();
    if ($entity_type_id = $route->getDefault('entity_type_id')) {
      if (empty($bundle)) {
        $entity_type = $this->entityManager->getDefinition($entity_type_id);
        $bundle = $route_match->getRawParameter($entity_type->getBundleEntityType());
      }

      $visibility = FALSE;
      if ($form_mode_name == 'default') {
        $visibility = TRUE;
      }
      elseif ($entity_display = $this->entityManager->getStorage('entity_form_display')->load($entity_type_id . '.' . $bundle . '.' . $form_mode_name)) {
        $visibility = $entity_display->status();
      }

      if ($form_mode_name != 'default' && $entity_display) {
        $access->addCacheableDependency($entity_display);
      }

      if ($visibility) {
        $permission = $route->getRequirement('_field_ui_form_mode_access');
        $access = $access->orIf(AccessResult::allowedIfHasPermission($account, $permission));
      }
    }
    return $access;
  }
}

wim leers’s picture

Issue tags: +blocker, +API-First Initiative
jamesdeee’s picture

OK, so progress on this is slooooow because I don't really completely understand what I'm doing. I think I'm halfway there - if anyone wants to jump in and make suggestions, please do. I'll put this into a patch later - even though it doesn't work I guess it's a good way to kick things off.

So, I've added an access method to the ConfigEntityBase class:

namespace Drupal\Core\Config\Entity;

...

/**
 * Defines a base configuration entity class.
 *
 * @ingroup entity_api
 */
abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface {
...
  public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {

    $access = parent::access($operation, $account, $return_as_object);

    if ($operation === 'view') {

      if ($this->entityTypeId === 'entity_form_display') {

        $entityType = new ContentEntityType([$this->entityTypeId]);

        $handler = new ConfigEntityAccessControlHandler($entityType);

        $userHasAccess = $handler->access($this, $operation, $account);

        if ($userHasAccess) {
          return new AccessResultAllowed();
        }

        else {
          return new AccessResultForbidden();
        }
      }
    }
    return $access;
  }
}

This gets called when a user hits the entity_form_display endpoint. In turn, it creates a new ConfigEntityAccessControlHandler, which just has an access method on it. I figure this is all it needs since the only thing to check is whether the user has the export content permission.

The ConfigEntityAccessControlHandler looks like this:

namespace Drupal\Core\Config\Entity;

use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\Role;

/**
 * Class ConfigEntityAccessControlHandler
 */
class ConfigEntityAccessControlHandler extends EntityAccessControlHandler {

  /**
   * {@inheritdoc}
   */
  public function __construct(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
    parent::__construct($entity_type);
  }

  /**
   * {@inheritdoc}
   */
  public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE) {

    $user = \Drupal::currentUser();

    if ($user->hasPermission('export configuration')) {
      return TRUE;
    }
    return FALSE;
  }
}

The main problem I'm having at the moment seems to be cacheing - if I clear the cache then hit the endpoint with an anonymous user, I get an access denied message. If I do the same thing with an admin user, I can access the configuration. If I then go back to the anonymous user and I can get to the config.

I don't really understand where the response gets cached. Can anyone help?

Anonymous’s picture

Status: Active » Needs review
StatusFileSize
new4.67 KB
 if ($user->hasPermission('export configuration')) {
      return TRUE;
    }
    return FALSE;

It can not affect the cache through a boolean values. Instead, an AccessResult is used with addCacheableDependency, or via methods that already contain necessary cache dependencies.

But we do not need to change a global handler in this issue. Only EntityFormMode and EntityViewMode. See issue summary (IS) and #7.

Well, I stole the work from the #2866666: Add proper access handlers for view and form displays for our entities, and moved the permissions from field_ui.module. But maybe we need in additional permissions like administer {$entity->getTargetType()} display mode.

wim leers’s picture

StatusFileSize
new1.04 KB

Hm, looking at the changes in #11, I wonder why we can't do this instead?

I have the feeling that I'm missing something very obvious…

jamesdeee’s picture

@vaplas - nice job! I was waaaay off the mark!

Yes, I feel like it would be a good idea to introduce that additional permission. But weren't the REST permissions heavily simplified between 8.2 and 8.3? Would this not be a step back?

Anonymous’s picture

#12: Hah, great point! Thanks, @Wim Leers! I tried to find a history of changes. And there seemed to be times when it was (see #2105557: Add an admin_permission to EntityType annotations to provide simple default access handling). But then we lost these entities (see #2031717: Make entity module not required). But now EntityFormMode and EntityViewMode are back in service!) So, why not return 'admin_permission' too?

#13: Thanks for the pleasant feedback, @jamesdesq. You are right, the additional permissions are not what we need here :)

wim leers’s picture

@vaplas I'm asking why #12 is not functionally identical to #11, but just much simpler?

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

@Wim Leers, I think #12 is very nice way, which in everything is better than #11:)
I was only confused by the phrase:

I have the feeling that I'm missing something very obvious…

For this reason, I only added references to the history of access changes to these entities in #14 (for me these links only confirm the correctness of #12 idea). And if no one has any flaws, let's change the status!

tstoeckler’s picture

Status: Reviewed & tested by the community » Needs work

At the very least field_ui_entity_type_build() needs to be updated to remove the (now-)duplicated setting of the admin permission.

So the permission we're referencing here is provided by Field UI, so I guess we would also have to move the permission, but the question is where to move it to?

wim leers’s picture

At the very least field_ui_entity_type_build() needs to be updated to remove the (now-)duplicated setting of the admin permission.

Indeed.

So the permission we're referencing here is provided by Field UI, so I guess we would also have to move the permission, but the question is where to move it to?

I think the answer is: field.permissions.yml? Nope, that does not work, because EntityFormMode & EntityViewMode live in lib/Drupal/Core… ugh, this makes total sense given this issue's title (which I hadn't read in some time). It also explains why today's code is what it is.

Re-reading the issue brings me back to @Berdir's comment in #7:

I guess that we need is to convert those into subclasses of EntityAccessControlHandler, similar to other entity types and then put it into the annotation of those entity classes. possibly even the same class/code if it's generic enough. And then update \Drupal\field_ui\Routing\RouteSubscriber::alterRoutes() to use them.

But I'm not sure if we can really replace that, as view/form displays are special in that they are auto-created, at least the default display. So that might not actually exist, but we still want to allow access.

So likely we can't actually replace them completely, just model the new code based on them.

@tstoeckler, do you agree with @Berdir's recommendations/thoughts?

tstoeckler’s picture

I think we are talking about different things here. I just now read the issue summary and that talks about \Drupal\field_ui\Access\ViewModeAccessCheck (and the form mode equivalent). Despite their name, those actually control access to EntityViewDisplay entities, however, not EntityViewMode (and the form mode equivalent). We already discussed how to make those use the entity access system at #2866666: Add proper access handlers for view and form displays and decided to punt that to #2882273: Make (Form|View)ModeAccessCheck use the entity display access control handler. That last issue also seems to be similar to what @Berdir suggested in #7, although to be honest, I don't grok that comment 100%.

The latest patch, however, does actually patch the EntityViewMode (and form) entity type, not the display one(s).

So not sure what this issue is actually about. Having written this, I'm pretty confused, to be honest.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Assigned: jamesdeee » Unassigned
Priority: Normal » Major
Issue tags: +Entity Field API

This is blocking 4 issues. All 4 of those are part of the 6 remaining blockers to #2824572: Write EntityResourceTestBase subclasses for every other entity type..

Bumping to major.


So not sure what this issue is actually about. Having written this, I'm pretty confused, to be honest.

Assigning to me to address that.

wim leers’s picture

Assigned: Unassigned » wim leers
wim leers’s picture

As an unexpected side effect, #2907654: No URL can be generated for the 'add-form' route of EntityViewMode and EntityFormMode entities unblocked #2843780 — see #2843780-32: EntityResource: Provide comprehensive test coverage for EntityFormMode entity. In that issue, we (or at least I) didn't realize there was a bug in the entity class for Entity(Form|View)Mode; now that that is fixed, that issue is also unblocked, hurray! And so is #2843781: EntityResource: Provide comprehensive test coverage for EntityViewMode entity!

We should still do this though, since it's nonsensical that the access logic for an entity type lives in a different module than the entity type.

This is now just not a blocker anymore. Also, #21 said this was blocking 4 issues, but it was blocking only 2, my bad!

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs work » Postponed (maintainer needs more info)
Issue tags: +stale-issue-cleanup

Thank you for creating this issue to improve Drupal.

We are working to decide if this task is still relevant to a currently supported version of Drupal. There hasn't been any discussion here for over 8 years which suggests that this has either been implemented or is no longer relevant. Your thoughts on this will allow a decision to be made.

Since we need more information to move forward with this issue, the status is now Postponed (maintainer needs more info). If we don't receive additional information to help with the issue, it may be closed after three months.

Thanks!

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.