Problem/Motivation
\Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin::isAdminPath() run a full router match and calls $this->pathProcessorManager->processInbound(). If path processors are not re-entrant then this can break routing. Unfortunately \Drupal\system\PathProcessor\PathProcessorFiles::processInbound() is not re-entrant so this language negotiation can break private files.
Proposed resolution
Remaining tasks
User interface changes
None
API changes
Hopefully none
Data model changes
None
Original issue summary
I successfully added a 'document file' element to a field file type and it's upload functionality works as well: the files get uploaded in the specified 'private files' location and is available on the files system (+ readable by the http server):
/var/www/html/webapp.local//files-private/2017-01/doc.pdf
Now, while trying to access the file via Drupal with the following url, the request respond with a 404 not found:
http://webapp.local/system/files/2017-01/doc.pdf
Symfony\Component\HttpKernel\Exception\NotFoundHttpException is reached with the message 'No route found for "GET /system/files/webform/media_accreditation_form/7/doc.pdf".
Why the '/system/files/{filepath}' route and its Drupal\system\FileDownloadController::download controller isn't reach is unclear to me.
But I found that Drupal\Core\Routing\RouteProvider::getRoutesByPath($path) is called with $path = '/system/files/2017-01/junge_koch_article_-_final.pdf' but return no route at all. The reason seems that there is no entry in the router table matching for path '/system/files/2017-01/junge_koch_article_-_final.pdf'.
Now, is it a bug in Drupal\Core\Routing\RouteProvider::getRoutesByPath($path) or there is something wrong while adding the '/system/files/2017-01/junge_koch_article_-_final.pdf' path in the router table?
| Comment | File | Size | Author |
|---|---|---|---|
| #29 | 2846379-2-29.patch | 5.51 KB | alexpott |
| #29 | 25-29-interdiff.txt | 842 bytes | alexpott |
| #25 | 2846379-25.patch | 5.57 KB | casey |
| #23 | core-83x-2846379-23.patch | 5.5 KB | esolitos |
| #14 | 2846379-14.patch | 5.53 KB | alexpott |
Comments
Comment #2
drikc commentedComment #3
drikc commentedFollowing up; it appears that in Drupal\system\PathProcessor\PathProcessorFiles::processInbound() $request->query->has('file') return TRUE which prevent to reduce and produce the correct path:
Comment #4
ramreddy.kancherla commentedHi @drikc,
Did you solve this problem. I am also getting 404 page not found when try to access private files for all users include admin user also.
Please let me know do you have any suggestions regarding this problem.
Thank you.
Comment #5
drikc commentedUnfortunately not (couldn't find more time on that at this moment). Moreover, I couldn't reproduce this on a genuine 8.2.5 installation...
Comment #6
alexpottDiscussed something very similar with @esolitos in IRC we discovered a problem with
\Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin::isAdminPath(). Breaking private files is at least a major.Comment #7
alexpottThe reason why the path processor is not re-entrant is \Drupal\image\PathProcessor\PathProcessorImageStyles::processInbound()
Comment #8
alexpottHere's a failing test.
Comment #10
alexpottCloning the request seems the simplest solution.
This is a straight up bug and should be fixed in 8.3.x
Comment #11
dawehnerThis is something which really should be kept in mind all the time. Good to know!
Comment #12
dawehnerThis looks great for me!
Comment #13
alexpottThinking about other places that call
->processInbound()\Drupal\Core\Path\PathValidator::getPathAttributes()This one is okay because earlier it does:
The same pattern is in
\Drupal\system\PathBasedBreadcrumbBuilder::getRequestForPath()tooI think we probably should update the documentation on
\Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound()to make this consideration known. So that people can discover this gotcha.Comment #14
alexpottBetter docs.
Comment #15
ramreddy.kancherla commentedThanks @alexpott. #10 patch worked perfectly for me.
Comment #16
esolitosHi, I wanted to work on this yesterday, but i was sick as hell, provided patch looks and works great, tested on 8.2.6, thanks
alexpottfor the quick work!Comment #17
drikc commentedDamn, I sweated on this; at least I wasn't on a complet wrong path...
Thank you, tested on 8.2.6...
Comment #18
esolitos@drikc
Don't worry you were not the only one, I spent 1 day figuring out the issue and then in half day (mostly) thanks to alexpott (and the other peeps in #drupal-contribute) we were able to narrow it down to the
LanguageNegotiationUserAdmin::isAdminPath()issue.Comment #19
swentel commentedThe sentence starting at 'Note, if this ..' is a bit weird if you read this out load.
Comment #20
esolitosAs far as i know this works in english, it's quite common writing "Please note, foo bar baz".
Comment #21
swentel commentedWell, it's more the second part: "and is not part routing, the request then the current request object must be" - the 'then' doesn't seem to belong there at all ..
Comment #23
esolitos@swentel I realized that after a while.
Here's a rerolled patch on 8.3.x with the comment change and updated to the latest commit.
Comment #25
casey commentedReroll for 8.4
Comment #26
mpp commentedThere's another side effect due to the next line in LanguageNegotiationUserAdmin:
$attributes = $this->router->match($path);When visiting a node translation (e.g. /node/fr/123) the content appears in the site's default language.
When commenting it out and returning TRUE or FALSE instead, the proper language is shown: french when FALSE, the users admin language when TRUE.
See match() in AccessAwareRouter:
The request object is created statically so I assume something's still going wrong within matchRequest():
Comment #27
catchComment #28
catchBumping this to critical alongside #2802403: Combination of language negotiation and path aliasing can cause a corrupted route cache, 404s - visitor-facing 404s make the site unusable and there's no workaround.
Comment #29
alexpottre #26 / @mpp that sounds like a separate issue.
Cut out an unnecessary
then.Comment #31
flocondetoileRecently, after updated Drupal Core to 8.5 all my private files became unreachable randomly. Really disturbing.
The cause seems to be that the route, at some point, /system/files/DOCUMENT was not rewrite by the PathProcessorFiles to /system/files (or may be the original path was added after the PathProcessorFiles ran on the path)
Tried patch #29 which fix this main critical issue. But this bugs occurs just after updating to 8.5 so I have some doubt if this is the same issue.
Comment #32
rvanderh1 commentedI recently updated to Drupal Core 8.5.4, and now links on my site that are in the form of http://www.somesite.com/index?sometoken=XXXX are having the "?sometoken=XXXX" part of the URL stripped out. The result can be failure to be recognized as an authorized user at the somesite.com site. Is this problem related to this thread? It seems that the problem occurs for URLs that are using the Redirect module, but the problem began occurring right after I updated Core to 8.5.4.
Comment #33
sealionking commentedafter applied the patch 25 then the patch 29, the private files read ok
thanks for you guys!
Comment #35
esolitosPatch from #29 should be the way to go.
Comment #38
larowlanFixed on commit
%s/that/than
Committed as bfed684 and pushed to 8.7.x.
Cherry-picked as 8480003 and pushed to 8.6.x.