Problem/Motivation
Bulk operations lists all available actions for the entity without checking access to that action.
web/core/modules/system/src/Plugin/views/field/BulkForm.php:
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$entity_type = $this->getEntityType();
// Filter the actions to only include those for this entity type.
$this->actions = array_filter($this->actionStorage->loadMultiple(), function ($action) use ($entity_type) {
return $action->getType() == $entity_type;
});
}
This appears to be because the action access is tied to access for the particular entity but not whether or not the user has access to do that type of action in general or not. ActionInterface::access() described here.
For example:
- Create a new role 'editor'
- Grant editor role 'view user information' but not 'administer users'
- Change /admin/people view access to 'view user information'
- As an editor, view /admin/people and all actions like cancel user accounts are listed as bulk action options
Proposed resolution
Creating a sample entity of the current type and passing it to the action::access() method to determine whether to show it in the bulk actions. I am not sure if creating a sample entity is a feasible approach.
Alternatively, we could provide a method like bulkAccess() on the action and allow the action to define when it is appropriate to be shown.
Or we could make the assumption that access to the confirm route means show it as a bulk action since we have this information:
@Action(
id = "user_cancel_user_action",
label = @Translation("Cancel the selected user accounts"),
type = "user",
confirm_form_route_name = "user.multiple_cancel_confirm"
)
Remaining tasks
Decide how to approach this.
User interface changes
Only show bulk action operations that the user has access to.
API changes
None (depending on route chosen to solve this).
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
bulk-operations-access.gif | 4.16 MB | scott_euser |
Issue fork drupal-2895262
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
scott_euser CreditAttribution: scott_euser as a volunteer and at Fat Beehive commentedComment #4
szeidler CreditAttribution: szeidler at Ramsalt Lab commentedWow, I also encountered the issue with a client today. It would be really nice, when we could provide only the actions in the selectbox for which the user actually has access too.
Comment #6
joachim CreditAttribution: joachim at Torchbox commented> Creating a sample entity of the current type and passing it to the action::access() method to determine whether to show it in the bulk actions. I am not sure if creating a sample entity is a feasible approach.
That's not really feasible unfortunately. It works with some entity types, but if an entity type expects certain fields to be set when a new one is created, it will fail. State Machine module has this approach -- see #2960370: Views filter will crash with entity types that enforce values other than the bundle for more on the problems this can cause.
> Or we could make the assumption that access to the confirm route means show it as a bulk action since we have this information:
I think that checking access to the action's confirm_form_route_name would be a good start.
If it turns out that it doesn't cover all cases, we'd need to look at adding a permission property to actions.
Comment #12
Jumoke CreditAttribution: Jumoke as a volunteer and commentedHi all, I encountered this problem as well. Anyone figured or concluded on a solution for this?
Comment #13
penyaskitoI don't think there is a one-fits-all solution for this use case.
There could be e.g. node grants for nodes preventing the action in some entities, but not all of them. Should we show the bulk action in that case? I think it depends on the context.
The current workaround is editing the view bulk actions and check those that you want to allow to perform, or do it with a form_alter if that depends on the actual user/permission/role.
Comment #15
mxr576Comment #16
mxr576Comment #20
morvaim CreditAttribution: morvaim commentedI created a work in progress merge request (test coverage is in progress).
The proposed solution is to introduce a new 'execute' operation on actions and add an access check on the BulkForm. And also in views grant access for every authenticated users for the 'execute' operation and later on other modules can also provide their own access check. For example the administerusersbyrole module could handle the access related to user actions.
See: https://git.drupalcode.org/project/administerusersbyrole/-/merge_requests/3
Comment #21
mxr576Additional thoughts on #20. We also try to narrow down the scope here. Currently, we have two possible problems to solve, as #13 already started to explain.
The system should be able to decide if an acting user can:
1. Execute an action _on an entity type independently from the current context_. This basically means a global yes or no.
2. Execute an action on a given entity (where the acting user, the given entity, and possibly other meta information are part of the current context) If the acting user can perform the given action on a given entity type (1).
Compared with problem 1 where an action is either explicitly allowed or not (rendered or not), the 2nd problem is also a UX challenge and possibly requires a more reactive UI that we currently have in Drupal core:
1. What should happen if I select Action X? Should we refresh the list of entities and filter out all those entities that the acting user cannot perform Action X for some reason?
2. What should happen when an acting user already selected Entity Y but Action X cannot be performed on that? Should we refresh the list of available actions based on selection?
Of course, we can still allow any combinations and fail at the end of the process with a result like "10/0 action performed successfully selected entities", which would be a bad UX and also DX.
So the suggestion is let's start small and first introduce a new operation "execute", "perform" or other Action config entities. That can be already used to define a global yes or no to perform an action. (See morvaim's Drupal core MR and Administer Users by Role MR)
Fixing problems 1 and 2 also going to be a BC nightmare, unless we move the target to Drupal 10.
Comment #22
mxr576@morvaim regarding test coverage, if this logic moves to system.module then
\Drupal\Tests\system\Kernel\Action\ActionTest
can be also extended to cover the new entity access check.A related views.module test can be also altered to ensure Views also uses the new access check, for example
user_batch_action_test_action
action (defined in\Drupal\user_batch_action_test\Plugin\Action\BatchUserAction()
) can be only performed by users who has permission X but not others. That action is currently used by\Drupal\Tests\views\Functional\UserBatchActionTest
Although, before we invest a lot of time in extending test coverage, first let's wait for some input from the community for the described and showcased idea.
(We should also change the component to system.module later if everybody agrees with the scope and resultion.)