Closed (fixed)
Project:
Drupal core
Version:
8.2.x-dev
Component:
editor.module
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
7 Jun 2016 at 15:12 UTC
Updated:
16 Mar 2017 at 08:17 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
cilefen commentedThis could perhaps be triaged up to major priority.
Comment #3
wim leersWe simply never added support for private files. You're the first to report/request this AFAIK, which explains how it went unnoticed all this time. Which is why this is IMO a feature request, not a bug.
Related reading: https://www.drupal.org/documentation/modules/file#content-accessing-priv...
Comment #4
boobaaWith great respect, it was your very own patch in #1932652-100: Add image uploading to WYSIWYGs through editor.module which added this feature stating that "(a)ny visible, writable wrapper can potentially be used for uploads". Later changes to this file (including, but not limited to, #2028109: Convert hook_stream_wrappers() to tagged services.) haven't really changed how this feature works in this regard: the private file system stayed there as an option.
I think we have two ways ahead now: either remove the possibility to upload files to the private file system via EditorImageDialog, or provide a way to allow downloading such files. As the first option would be a regression by now, I'm clearly determined pursuing the second one.
Comment #5
wim leers#4: where exactly did I say that? I don't see it in comment #100 that you linked to. I'd love to read my own thinking about this from back then :)
You assigned this to yourself, so it sounds like you want to take this on. Let's start with writing a test that shows you can upload files while using the private file system, but you can't download them. That would then make it more clear what else we need.
Comment #6
boobaaWell, that's quoted from http://cgit.drupalcode.org/drupal/tree/core/modules/editor/editor.admin.... and it's still there, untouched ever since it was introduced. :)
Attached is a first pass for a patch, without any test for this time. FTR, here are (some) steps for reproduction.
$settings['file_private_path'] = '/tmp/private';).Without the patch, the first test passes while the second and the third fail. With the patch, all tests pass.
As these steps include some client-side magic, I'll have to learn how to write tests for that. I'm unassigning this to allow anybody beating me on this. OTOH, I don't know how to test this in 8.1.x, so when everything seems to be OK maybe we should just wait for October's 8.2.0?
(One might argue why should "Restrict images to this site" be disabled, but that's a different issue. Basically
_filter_html_image_secure_process()improperly recognizes URIs of private files as non-local.)Comment #7
wim leersAha! Thanks for pointing that out :) (I was looking in the issue comments, that's why I was not able to find it.)
Wow, great first pass! :) Thanks so much!
The steps indeed include JS if you do it manually. But you can just as well do it without JS. You can use
File::create([…])->save()to "upload" a file to theprivate://stream. Next, you can interact with the form without JS like this test is already doing:\Drupal\Tests\editor\Kernel\EditorImageDialogTest::testEditorImageDialog().RE: — yep, that's a known bug. +1 for excluding that from the test, so that it doesn't interfere.
Comment #8
boobaaIt turned out there's no real need to do the non-JS magic like
\Drupal\Tests\editor\Kernel\EditorImageDialogTest::testEditorImageDialog(). If the body field's value is forged just like CKEditor would do it, that does the trick for us. And hey, we're not testing CKEditor here: everything we actually want to test has nothing to do with JS.OTOH, the test file itself needs to be created with that
file_put_contents()call: without that, we'd get 404 with or without the actual fix, which is somewhat expected. :)Comment #11
boobaaTestbot hiccup?
Comment #12
wim leersRequested new tests for both — let's see :)
Comment #15
wim leersThe failure root cause:
This is why the patch always results in a fail: the namespace is wrong.
Also… let's please not do this — installing the "Standard" install profile is very very time consuming. We should only install the necessary modules, and then manually create the necessary configuration.
This works, but is very slow. There's no reason for us to do this via the UI. We can just create the necessary configuration directly.
WebTestBase? It's very slow. Let's useKernelTestBase. In fact … I think you can just add a new test method to\Drupal\Tests\editor\Kernel\EditorFileReferenceFilterTest:)Comment #16
boobaaThanks for these hints, they all make sense, but it looks like I can't
file_put_contents('private://llama.jpg', $this->randomMachineName());in a KernelTestBase-derived class. I couldn't even find an already-existing example for this. Could you please give some ideas how to create a file in the private:// scheme in a KernelTestBase-derived class? Without such a file, I don't see any way to actually do any tests.Comment #17
boobaaHaving a working, but slow web test is still better than not having a fast kernel test which works, I think. I couldn't find a way to create a file in a kernel test in the private:// scheme, and I couldn't find an example in core which does this.
Attached is still a web test, but this time with the testing.profile, and pulling in the config instead of creating it via the UI. IOW, all concerns from #15 have been addressed by this but the last one, for the above reasons.
Comment #20
boobaaOkay, let's not add config for editor tests to filter tests. :)
Comment #23
boobaaOkay, then let's introduce a dedicated test module to avoid messing with other already-existing things.
Comment #27
wim leersGreat progress here! :) However, could you provide interdiffs from now on? https://www.drupal.org/documentation/git/interdiff
Comment #28
boobaaOkay, will do – here are some interdiffs for the already-existing patches for your review convenience. Setting the issue to "Needs review" as the only failing patch is the one that should fail (tho it has an unrelated fail as well, for some unknown reason). Hoping this won't trigger the testbots.
Comment #29
wim leersNit: s/array()/[]/
More importantly: let's not call a deprecated API function.
Woah!
This comment seems to be dangling. Does this belong with the
!isset($file)?"which fields" vs "$references" -> very confusing.
node/add form->node creation formThis is very confusing.
I think that:
would help a lot
This extra complexity only exists here because this was based off of
file_get_file_references().This is only ever called with
FIELD_LOAD_CURRENT. So let's only support that.Let's prefix this with an underscore, to signal this is private.
Let's also add
@internal.Both of those things ensure we can change this function's implementation and interface over time. This is NOT a public API.
In fact, why do we need to get all references? We never use it. All we need to know is whether the file that is being downloaded is in fact used, and used by
editormodule somewhere.The reason
filemodule'shook_file_download()callsfile_get_file_references()is because it provides the data it needs. It provides much more than is necessary, but it also contains what is necessary. So that's what it uses. But in fact that module could also just look at thefile.usageservice's entries for thefilemodule.This entire function with all its complexity could then go away.
And instead we'd just have
in
editor_file_download().*So* much simpler.
Or what am I missing?
Comment #30
boobaaThanks for all your precious input!
I've addressed everything mentioned in #29. Some notes:
For 2nd bullet point: Indeed. (And hey, this is present in the File module, too.)
For 3rd bullet point: This comment describes why we aren't doing any temporary-specific wizardry here (and where's the place to look for such).
For 9th bullet point: Yes, you're absolutely correct – and this made the code easier and the patch much smaller.
OTOH, I wonder what should be next if this patch gets committed. Most of it has been borrowed from the File module, and most of the improvement ideas listed here would apply there as well. (If I'm not mistaken, even the revision-related parts.) Probably a followup issue for cleaning up File module in the same sense?
Comment #32
wim leers#30: awesome!
RE: cleaning up File module in the same way: we can't do that; there could be contrib modules relying on this code, since the code there is not declared internal, hence it's a public API.
P.S.: upload the test-only patch first, then testbot won't mark this issue needs work :) Silly & annoying small thing you have to know when using d.o.
This now pretty much looks ready, but just needs a bit more test coverage.
We shouldn't add new
WebTestBasetests. Please useBrowserTestBaseinstead – see #2469723: New PHPUnit based classes added for testing: BrowserTestBase and JavascriptTestBaseThis is only testing one case — the most common case.
It's not yet testing the case of a file not yet being permanent, yet uploaded by the current user.
Thanks for your super awesome work here!
Comment #33
wim leersOh and since this is a bugfix, this can go in 8.1!
Comment #34
boobaaComment #35
boobaaComment #37
wim leersThis looks great! Thanks a lot for your awesome work here! :)
Comment #39
boobaaTest ran fine after testbot hiccup.
Comment #40
gábor hojtsyComment #41
gábor hojtsySorry I did not use the proper tag for D8 Media.
Comment #43
gábor hojtsyLooks like a retest of this passed fine above :)
Comment #45
wim leersRetesting again. Random fail again.
Comment #46
duaelfr+1 RTBC
Tested with Core image plugin and with Editor File contrib one.
Thank you all for your work :)
PS: private images are still considered as external images by the "Restrict images to this site" filter with this patch. Did someone open a followup issue?
Comment #48
wim leersRandom fail in
ConfigImportAllTest.Comment #49
wim leersComment #50
boobaa@DuaelFr in #46: Just posted the followup issue: #2772617: Handle files uploaded to the private storage as internal.
Comment #51
alexpottWhat's the purpose of this get? Nothing is asserted afterwards? If it is necessary for the test it needs a comment.
This logic in the if does not seem to match the comment.
Isn't the matching logic:
If we can make this simpler by breaking it up into separate ifs i'd encourage that.
Comment #52
wim leersfile_file_download():Still NW for point 1.
Comment #53
alexpottOh ok re #51.2 I'm always wary of security logic in ifs that requires a big comments and quite a bit of think to understand how it works.
Comment #54
wim leersJust fixed point 1. Back to RTBC, because it's just a nit. Passed locally.
Comment #57
alexpottThe first comment seems to be in direct opposition to the later code. If there's nothing to do here about them why do we have...
($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())- if file_file_download() handles this - is this necessary?Comment #58
wim leersThe difference is that the temporary file handling (the first section) is not module-specific. The second section (the "stop processing" bit) is module-specific.
refers to:
Which as you can tell does not list anything module-specific.
The second section however does module-specific things:
file.module'sfile_file_download()callsfile_get_file_references(), which does:Note the key
'file', this corresponds tofilemodule. This means this is only granting download access to files owned by thefilemodule.The corresponding second section in
editormodule'seditor_file_download()checks not the'file'key, but the'editor'key: it grants download access to files owned by theeditormodule.Comment #61
alexpottCommitted and pushed f2ed11a to 8.3.x and 6e46381 to 8.2.x and 46664f6 to 8.1.x. Thanks!
Fixes on commit:
Comment #63
alexpottComment #67
alexpottI've reverted this change because after commit I suddenly got worried that this does not implement the correct security behaviour. We if we have a private file referenced in a node should we grant people access to it?
If this is the correct solution. We should test that the anonymous user still does not have access after doing this.
Comment #68
alexpottAlso this grants access to the file regardless of whether the user has access to the particular entity afaics.
Comment #69
wim leers#67:
That code is simulating a call to
\Drupal\file\FileUsage\FileUsageBase::add(), which is called by_editor_record_file_usage(), which in turn is called by eithereditor_entity_insert()(when the image is inserted when the entity is first created) or byeditor_entity_update()(when the image is inserted when the entity is updated).So did you quote the wrong code?
In any case, you're right that we need that test.
#68:
Ugh. You're totally right. And that's what the addition in #7 proved.
This is the problem:
This means we're not at all checking whether the user can access one of the
editor-referenced entities before it grants access. I should've questioned it more; this patch has contained that since the very beginning. Sorry for missing that, Alex. The tests seemed right and passed, but thankfully you noticed just in time that this should be testing one more thing.Also: this entire API really needs an overhaul in Drupal 9, because it's extremely brittle :/
This patch just expands the test coverage. This should fail.
Comment #70
wim leersForgot the interdiff.
Comment #71
wim leersAnd here's the fix. It's 80% copied from
\Drupal\file\FileAccessControlHandler::checkAccess(), 10% copied fromfile_file_download(). Sadly we can't reuse any of the existing code without enormous refactoring, so this feels simpler then. The logic is trivial.Given that the logic is trivial and 90% copied, I'm going to self-RTBC, so Alex takes another look at it.
Comment #74
alexpottCommitted 05d832e and pushed to 8.3.x. Thanks!
Leaving at patch to be ported for eventual cherry-pick to 8.2.x once 8.2.0 is released.
Unused uses fixed on commit.
Comment #77
alexpottCommitted e4aec7c and pushed to 8.2.x. Thanks!
Comment #79
mabuweb commentedI've applied the patch form #71 to my Drupal 8.2.1 installation. It does'nt work for me, all inline images still get an access denied.
I think there is a problem with the editor_file_download($uri) hook.
The $usage_list contains an array like this: $references[$usage->module][$usage->type][$usage->id] = $usage->count;
The line
$referencing_entities = entity_load_multiple($entity_type, $entity_ids);expects $entity_ids to be an array of id's, but it isn't, its an associative array like this: [id1=>usage_count1, id2=>usage_count2, ..., idn=>usage_countn,]
A simple way to fix this is to apply array_keys($entity_ids):
$referencing_entities = entity_load_multiple($entity_type, array_keys($entity_ids));Applying this fix the patch works for me.
Comment #80
wim leers@mabuweb: Next time, please create a new issue. Nobody saw your comment because the issue was already closed. Somebody just reported the same bug, and included a patch to fix it: #2831392: Text Editor module uses file.usage service incorrectly.
Comment #81
boobaaAs #2831392: Text Editor module uses file.usage service incorrectly is 403 for me, I'm attaching what @mabuweb suggested in #79 here as a patch.
Comment #82
boobaaCreated another issue for this: #2841761: Proper private file support for images uploaded via EditorImageDialog.
Comment #83
maxilein commented@Wim Leers #80
Is your link 403 on purpose? I cannot access it - just like #81
Comment #84
quicksketch@maxilein: That issue is a 403 because it has been unpublished. That issue revealed a security vulnerability, and so it was unpublished in accordance with the Drupal security policies. It was fixed just today with the release of Drupal 8.2.7. See https://www.drupal.org/SA-2017-001. So for now if you need that fix the easiest thing to do is to update to the latest version of Drupal. You could pull the fix out of the Git history of course, but it's policy not to unnecessarily publicize the exact code that contains vulnerabilities.
Comment #85
maxilein commented@quickscetch
Thank you for the clarification. Makes sense now!