Problem/Motivation

With certain configuration, private files may become inaccessible or improperly redirect if an unauthenticated user attempts to access a private file.

Steps to reproduce

1. Install r4032login module with default configuration, log in to the site as a privileged user, and set up a second privileged account.
2. Under "Basic site settings" (/admin/config/system/site-information) uncheck "Display access denied message on login page"
3. Upload two private files to unpublished content (e.g. to a File field with cardinality >1, or to two separate nodes)
4. Attempt to download files as the authenticated user; observe that the downloads complete successfully.
5. Open an incognito window and paste a link to file #1. Log in to the second account when prompted. Your browser will report an error (Firefox says "The page isn’t redirecting properly")
6. Back in the main window attempt to download either file. You will get the same browser error, and attempting to access file #2 will instead redirect you to file #1 with the error. These errors persist until you clear Drupal caches.
7. Repeat step 4. Repeat step 5 but do NOT log in, move on to step 6. Observe that the issues in step 6 occur exactly as before. Thus, an unauthenticated user merely following a link to a private file can mess up private files access even if they do not attempt to log in.

Proposed resolution

Credit to @gbeezus for discovering the solution in the attached patch. Note that this one-line change adds extra cache context to code added in the parent issue which addresses caching issues more generally.

Comments

caesius created an issue. See original summary.

caesius’s picture

Priority: Normal » Major
Status: Active » Needs review
nixou’s picture

Status: Needs review » Postponed (maintainer needs more info)

I cannot reproduce this problem.

An issue about private files has been fixed recently, see #3222717: Redirecting to a file with spaces in the name results in 404 and #3200138: Leading slash for destination param should not be stripped, causes URL encoding issues.

Can you retry on latest dev to see if you still reproduce ?

caesius’s picture

Status: Postponed (maintainer needs more info) » Needs review

I downloaded the latest dev, re-ran the reproduction instructions on my sandbox site (which I'd reverted to a state from before I installed the module originally) and tried again, I'm still running into the same issue. This is happening both in a local development environment running Docker (nginx) and on a production site hosted by Pantheon.

One thing that may be important which I forgot to mention is that in both environments the private files directory is sites/default/files/private

tgauges’s picture

I could reproduce this exact problem and the patch with the additional 'user' cache context fixed the problem for me.

andileco’s picture

This patch also worked for us (the problem started occurring around Drupal 9.2.9, FYI). +1 for RTBC.

kevin.dutra’s picture

Assigned: caesius » kevin.dutra
StatusFileSize
new1.89 KB

These adjustments to the test should demonstrate the issue. This patch only includes the test adjustments, so testbot should confirm the issue exists by coming back with a failure.

Status: Needs review » Needs work

The last submitted patch, 7: cached-redirects-3239514-7--TEST-ONLY.patch, failed testing. View results

kevin.dutra’s picture

Assigned: kevin.dutra » Unassigned
Status: Needs work » Needs review
StatusFileSize
new2.6 KB

And here is the test adjustments + a fix.

This fix is different than the initial proposal of adding the 'user' cache context. There are a couple reasons for the adjustment:

  1. That may work for the case where the logged in user is having a problem, but there would still be issues for 2 unauthenticated users trying to access files should still run into issues because they're both "the same user".
  2. The 'user' cache context makes for so much variation that cacheability may be fairly low. 'session.exists' is just a boolean for whether you're logged in or not, so there should be a higher hit rate. Also including 'url.path' since the response for file 1 is not necessarily the same as for file 2.
krlucas’s picture

We are experiencing similar issues with private files and r4032login redirects.

I think the addition/tweaking of cache contexts makes sense but I would disagree that session.exists is best. Anonymous users can have sessions but I can't think of scenario for the same URL where two anonymous users should get a different response from this module or where an anonymous-user-with-session should get the same response as an authenticated user

Indeed by default Drupal won't render/dynamic-page cache anything with the user cache context.
https://git.drupalcode.org/project/drupal/-/blob/9.4.x/sites/default/def...

But user.role:anonymous should allow some caching but with the least amount cache fragmentation.

Additionally, I think the url.path cache context should be changed to just url so that the query string is also considered. In the case of private files, we've observed that sometimes Drupal will rewrite URLs like /system/files/path/to/a/file to /system/files?file=/path/to/a/file for some reason (in our case it happens when specifying a private file as a Redirect module destination).

kevin.dutra’s picture

StatusFileSize
new2.6 KB
new698 bytes

Sounds reasonable to me. Here are those tweaks.

nixou’s picture

StatusFileSize
new19.19 KB

I spent again a lot of time trying to reproduce this until I realize that the problem occurs only when the Dynamic Page Cache module is enabled.

The provided test helped me a lot to better undestand the situation, thanks for that !

So there is 2 problem actually :

Problem 1 : Dynamic Page Cache cache the redirection without considering the access of the user since we do not provide access cache context

So if a user accede to a page he has not access to, the redirection will be set in cache for all users, authenticated or not.

Problem 2 : Dynamic Page Cache manage its cache per route and not per url

So when we return our redirection, we return it for all url corresponding to this route.
For example in the case of this issue we make a redirection for the "system.files" route for the "private" scheme.
So as soon as we make a redirection for a private file, all private files become inaccessibles.

This is a more general problem actually that juste private files access.

We could try to determine our own cache tags and contexts but there is too much cases to considere so probably we should better try to follow what the core do.

An easy way would be to base our class on the default 403 event subscriber and retrieve the cache metadata for the original response provided by the core.
We just need to add the client error tag then, as the core will not do it since our response status code is not necessary a 4xx code.

With this solution we are mostly sure that our redirection is cached exactly as a default 403 would be so I hope this will solve all caching issues.

Here is the new patch containing the test and the new cache tag approach.

If test pass I will merge it directly.

Status: Needs review » Needs work

The last submitted patch, 12: 3239514-12.patch, failed testing. View results

nixou’s picture

Status: Needs work » Needs review
StatusFileSize
new4.44 KB
new5.69 KB

The tests don't pass but after thinking about it I wonder if it is really necessary to get the cache metadata from the 403 page itself.

We could more simply apply the cache metadata of the initial response and thus those of the access result.

In the case of a private file, the behavior of the core is to not cache the response in Dynamic Page Cache so by reusing the same cache metadata we will have the same behavior and therefore our redirection will not be cached either.

This avoids that our module applies a different behavior than the one the core would have applied if it had displayed the 403.

If later the core evolves and a 403 on a private file is finally cached with a variation by url or by role, then this module will automatically follow this evolution.

I re-upload the 2 patches in this simplified version with test only and the patch to see if everything is ok now.

The last submitted patch, 14: 3239514-13-test-only.patch, failed testing. View results

  • Nixou committed b4acaa2 on 2.x
    Issue #3239514 by kevin.dutra, Nixou, caesius, gbeezus: Private files...
nixou’s picture

Status: Needs review » Fixed

Thanks to all !

Committed to 2.x-dev, will be released in 2.1.1.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.