Problem/Motivation
We discussed this in private first and agreed to make it a public issue.
With views caching turned on, the Workbench access section fails to bubble cacheability metadata resulting in cache poisoning
Steps to reproduce
1. Enabling the module
2. Creating a view that lists content for users to edit
3. Create an access scheme using taxonomy
4. Add two terms (Sections) to the created vocabulary
5. Add the 'Workbench access section' filter to the view created earlier and ensure the view has caching enabled (tag based)
6. Create two editors, put one in each section
7. Create content in section one
8. Visit the listing page as user in section one and see your item listed
9. Visit the listing page as user in section two and you'll see the view that was rendered for the previous user (cached version)
Proposed resolution
Add a WBA cache context
Ensure the filter emits the correct cache metadata
Remaining tasks
All of the above
User interface changes
API changes
Data model changes
Issue fork workbench_access-3201775
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:
- 3201775-the-views-filter
changes, plain diff MR !4
Comments
Comment #2
larowlanPlease grant credit to ramya.shankaralingam who discovered this
Comment #4
agentrickardComment #5
agentrickardI assume that even though the list is wrong, the edit links still don't work. Is that correct?
Comment #6
larowlanYes that's correct
I can tackle this next week
Comment #7
iansholtys commentedConfirming that this is still an issue.
Comment #8
joshua1234511Comment #10
agentrickardGiven that our main support cases are 'user' and 'node', I am not sure why 'taxonomy_term' is in this list.
And if 'taxonomy_term' is in the list, shouldn't any base entity that uses a WA field? That would suggest that we do a dynamic lookup of views tables potentially affected by this field.
[EDIT] Should we instead be checking for the presence of one of our controlled field or filter types in the view?
Comment #11
agentrickardAnd a patch.
Comment #12
agentrickardInterdiff for the last patch.
Comment #13
agentrickardTry again to load the right file.
[Edit[ Here's a reference to the technique, which is used in the "Fields used in views" report.
https://api.drupal.org/api/drupal/core%21modules%21views_ui%21src%21Cont...
Comment #14
larowlanshouldn't this be keyed by the view ID, you could build two views in the one request, they both don't necessarily have that handler
We should be using $row->getEntity here
There's a fair bit of work going on here to track tags, I wonder instead if we should build a context user.section_assocations which by name (user.) is less specific than user, and ignored if a per-user context is present.
This would be similar to the user.permissions context which is just a hash of the permissions the user has, so is automatically invalidated every time new permissions are granted.
To calculate the context, we would just use an entity query to get all section association IDs for the user, and then join them, e.g implode with ':' and then generate a hash using the crypto apis.
We'd then have our views plugins return that in their :getCacheContexts method (return ['user.section_assocations']).
We'd then need an update hook to find any view that has one of these plugins, and update it to add that context. Lots of examples in core of update hooks using the views updater service.
I think we also need some tests here to prevent this regression and ensure its fixed
Comment #15
agentrickardComment #16
agentrickardComments in order:
1. Nice catch on the static array. Fixed.
2. ResultRow has no getter method. https://api.drupal.org/api/drupal/core%21modules%21views%21src%21ResultR...
3. I'm not sure how complex that part would be, so left it out. This should work.
As to tests...I am not quite sure how to test this. I think the answer is:
* Create two users
* Assign users to different sections in WA
* Create a cached WA view that shows something filtered by sections
* PRE-patch -- confirm view shows the same output for both users if caching enabled
* POST-patch -- confirm view does not show the same output for both users if caching enabled
Comment #17
agentrickardI also wonder if we should just cache this per-user. More granular caching is good for performance, but these are administrative views where a cache would be less valuable.
Comment #18
agentrickardI just reviewed the test tests/src/Functional/ViewsFieldTest.php which checks for this condition -- but it uses Exposed filters, which removes the issue.
The bug does not appear in cases where the user is not assigned to any sections.
Comment #19
agentrickardI would note that I don't understand the use-case for not exposing the filter.
Note: Using an exposed filter fixes the cache *only* if an argument is present, which indicates a fail in our current test.
e.g. the page /cache-test will show the filters of the prior user, but https://workbench.ddev.site/cache-test?workbench_access_section__section... will not.
That suggests that we can just update the existing test in order to prove/fix this issue.
Comment #20
agentrickardSo we don't catch this in the existing test because it seems that the drupalLogin() process resets the cache. Otherwise we would catch this in tests/src/Functional/ViewsFieldTest.php.
In that test, User1 gets these cache tags (and all nodes)
User2 gets these (and only the proper nodes in their sections):
I am uncertain how we can write an automated test for this, given the behavior of the current tests which should fail according to this issue.
That suggests that we might need to commit this without a test. Or perhaps write a kernel test?
Comment #21
agentrickardWith manual testing, the patch in #16 does not work.
Nor does the patch in #8.
Comment #22
agentrickardResearch:
- https://drupal.stackexchange.com/questions/274714/adding-a-cache-tag-to-...
The addCacheTags() method may not be available, since it seems to be related to VIews UI -- https://api.drupal.org/api/drupal/core%21modules%21views_ui%21src%21View...
Comment #23
agentrickardIt is sufficient to add a user cache context to the View. Adding more complex rules seems self-defeating and over-optimization. If people wish, we can address that in a follow-up.
That decision radically simplifies the patch, which seems to test just fine.
* Install the default workbench access scheme (drush wa-test)
* Configure the field and access scheme rules
* Generate some content
* Create a view of content that filters by Workbench Access section
* Assign two users to different sections
* Login as both users in separate browsers
* Check the view render before and after clearing cache for both users
* Re-assign a user to new sections -- check render
* Remove a user from a section -- check render
Notes from previous comments:
* We do not need an update hook, since this is a runtime alter hook.
* No interdiff, since the new patch is radically different.
Comment #24
agentrickardNow we just need to see if this is covered by the current set of tests.
Comment #25
agentrickardIt should be covered -- but I see no examples in core Views that actually test the rendered output for different users. Their tests just check that tags / context exist, so we could test that.
Note also that I wondered if we should not add this context to uncached Views, but this test suggests otherwise:
Views/tests/src/Functional/Plugin/CacheWebTest.php
Comment #26
agentrickardAnd a patch with proper testing...
Comment #27
agentrickardPrevious patch is malformed.
Comment #29
agentrickardHere's a test-only version that shows the change.
Comment #31
agentrickardSo the patch in #27 looks good-to-go.
Comment #33
agentrickardCommitted.
Comment #35
fenstratJust a note for anyone who might be using this with an entity reference selection view, you'll probably hit a fatal
Uncaught PHP Exception Error: "Call to a member function bundle() on nulldue to the way WBA no implements hook_views_post_render():I can't see any issues here, it seems legit to add cache tags onto the output array. Instead it's a core issue, for a fix see #3341448: EntityReference ViewsSelection::stripAdminAndAnchorTagsFromResults() should call Element::children($results)