Problem/Motivation
In an image field's configuration, you can specify a default image to be displayed as a fallback if the content editor does not upload an image to that field.
When an image field's Upload Destination is set to the Private file storage destination (i.e.: the private:// stream), attempting to view (i.e.: download) the default image returns an HTTP 403 Access Denied, resulting in a broken image on the page.
Steps to reproduce
- Add an image field to a content entity type. In the new image field's Field Settings, set its Upload destination to
Private files. In its settings, upload a default image. - Add an image field to a content entity type. In the new image field's Field Settings, set its Upload destination to
Public files. In its settings, upload a default image. - Create a new instance of that content entity. Leave both image fields empty.
- Expected behavior: The default image for both fields are shown,
- Actual behavior: The default image for the field whose Upload Destination is set to "Public files" is shown. The default image for the field whose Upload Destination is set to "Private files" is broken.
Inspection in the browser's Network Console shows Drupal responds to the browser's request with an HTTP/403 Access Denied response. Further inspection of the cause of the 403 shows that a \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException is thrown from \Drupal\image\Controller\ImageStyleDownloadController::deliver() because $this->moduleHandler()->invokeAll('file_download', [$image_uri]) fails to return any headers (i.e.: indicating access denied). In particular, the image module's implementation of hook_file_download() (i.e.: image_file_download()) does not handle a case for default images.
As of 2020-05-29, this happens on 9.1.x and 8.8.x.
Proposed resolution
Modify image_file_download() to handle the case for default images, by granting access if the image URI that is being requested happens to be the default image for at least one field that the current user has 'view' access to.
Remaining tasks
Update issue metadata, summaryRe-roll patch from #24>- Review and feedback - in particular, is the access check that we are making sound and complete?
- RTBC
- Maintainer review, feedback
- Commit
- Backport?
User interface changes
None.
API changes
None.
Data model changes
None.
Release notes snippet
To be determined.
Original report by claudiu.cristea
On an image field where the uploaded destination is set to private:// stream, the default image is returning 403.
If you encounter a WSOD while manually testing, see #2799837: WSOD when changing uri_scheme and setting a new default image at the same time for an image field..
| Comment | File | Size | Author |
|---|---|---|---|
| #166 | 23_01_11327_D11.patch | 102.69 KB | dhavalpanchal |
Issue fork drupal-2107455
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 #1
claudiu.cristeaHere's a test revealing this bug.
Comment #2
claudiu.cristeaAh, the patch.
Comment #4
vvvi commented#2: private-default-image-2107455-1.patch queued for re-testing.
Comment #6
mgiffordComment #8
duneblThis bug is still there in 8.2.
The only way to use default images with private stream is to set the stream of the image field as public and upload the default image. After that you can set the field stream as private...
Comment #10
shaundychkoComment #11
shaundychkoThis also builds on the tests in #2.
Comment #12
joelpittetThis looks like a nice fix and improvement to the test coverage in these cases. Thanks @ShaunDychko and @claudio.cristea
The solution could be merged as the fix is duplicating a bunch of code for
if (strpos($path, 'styles/') === 0) {. Not sure if the refactor is needed but thoughts?Comment #13
alexpottDo we really need a new permission for this? If the user has access to the field then they should have access to the default - but how are you going to know what field... ugh.
Comment #14
alexpottI think we might have to work out where the default image comes from and check if the user has access to that.
Comment #18
kingdutchReroll against 8.5.x.
core/modules/image/src/Tests/ImageFieldDisplayTest.phpmoved tocore/modules/image/tests/src/Functional/ImageFieldDisplayTest.php.Comment #19
joseph.olstadTriggered 8.7.x automated tests
Comment #20
b-prod commentedThe patch in #18 fixes the issue on an image field set to private.
Comment #21
alexpott#13/#14 have not really been addressed. Adding a new permission for this feels a bit off when we already have the field access system.
Comment #24
drclaw commentedHere's an attempt to check the default image's field access (addressing #13/#14). Right now I'm only checking the field access with
Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess(). Not sure if we should also be creating a stub entity and checking access against that. For example, right now, if you don't have the "access content" permission, you'll still be able to see default images.... seems wrong?Also the tests need work, but I wanted to see if this is the right direction before heading down that road.
Comment #27
Mike.Brawley commented#18 worked for me on V8.8.5
Comment #28
pradeepjha commentedRe-rolling #18 patch against 9.1.x-dev version.
Comment #29
aleevasComment #30
sharma.amitt16 commented@pradeep rest looks good but the minor change required.
Please try to use a short array instead.
forex.
Comment #31
jyotimishra-developer commentedComment #32
jyotimishra-developer commentedAssigning to myself, as I am working on this.
Comment #33
jyotimishra-developer commentedComment #34
pavnish commented@jyotimishra123 Can you Please change some code.
#1
1. if (strpos($path, 'default_images/') === 0) to if (strpos($path, 'default_images/') === FALSE)
2. $access_private_default_images_user = $this->drupalCreateUser(array('view private default images')); To $access_private_default_images_user = $this->drupalCreateUser(['view private default images']);
Comment #35
jyotimishra-developer commentedComment #36
jyotimishra-developer commentedComment #37
pradeepjha commentedComment #38
pradeepjha commentedComment #40
pradeepjha commentedComment #41
pradeepjha commentedHi @pavnish -> if (strpos($path, 'default_images/') === 0) to if (strpos($path, 'default_images/') === FALSE) is throwing error. So I've left this change.
Comment #42
jyotimishra-developer commentedHi, the patch in #41 applied cleanly on Drupal instance 9.1.x and worked as expected!!
Comment #43
rajneeshb commentedPatch #41 working as expected.
Comment #44
xjmEveryone, please read the issue before rerolling patches. #13 and #14 have still not been addressed.
I am removing credit for all the rerolls after #24 because they are not working toward solving the issue. Please read #13 and #14, and try to build on #24. Thank you!
Comment #45
mparker17Update the title and issue summary in preparation for a patch.
Comment #46
mparker17(hid some irrelevant file uploads)
Comment #47
pavnish commentedComment #48
mparker17@pavnish, I'm working on the issue right now; I'm just working on the tests. Sorry for the delay.
Comment #49
mparker17I apologize for the delay - I don't get a lot of practice writing functional tests in PHPUnit.
Here is an attempt to update @drclaw's patch from #24 (which attempts to address @alexpott's concerns in #13 and #14).
Reviewers; please verify the completeness and soundness of the access-checking logic, since that has implications for security. In particular, this only checks if the current user has 'view' access to the field; not whether the user has 'view' access to the entity type(s) that the field is embedded in. (Turns out it is difficult to create a user that does NOT have access to 'view content' (i.e.: nodes) in functional tests, so its non-trivial to write a test for that case)
I've also included interdiffs between:
Feedback welcome.
Comment #50
mparker17Should set this to "needs review" and unassign myself.
Comment #51
Mike.Brawley commentedI have applied patch #49 and from what I can tell its fixed my issue. I cant say anything about the code but on the front end it all seems to work.
Thank you to everyone that has been working on this issue.
Comment #52
bas123 commentedI messed with this and tried everything I could. Even the Patch failed locally at first. But then I tried a different syntax and it worked out!
I ran the patch locally and then on two online sites and all seems to have been resolved, in fact I was able to colorize the gray Default background first to match my theme color, and it worked like a charm!
So chalk that one up as one more successful field test!
Now my only suggestion is that whomever is in charge of this thread do a simple step by step and clarify and simplify the process!
It really took me a while to figure out which of the patches in #49 I was to apply especially because that seemed to be the most recent and was marked as needing further testing!
And it was unclear which of the patch commands (from https://www.drupal.org/patch/apply) I needed to apply, but
git apply -v private-default-image-is-inaccessible-2107455-24.patchrun from my root (/html/) folder did the trick after clearing caches!Note: I used "private-default-image-is-inaccessible-2107455-24.patch" which succeeded. I trust that since it worked that that was the correct one and if not, should I run a different one over ??
I did notice in the production site that one member's profile photo (Open Social Demo User) still showed up on the activity streams, but since she was a demo user, isn't that because their images are derived from a different (Demo) source?
So, I deleted the photo which was correctly replaced with the default, checked the stream as a visitor and indeed the default photo was in place.
Then I uploaded a copy of her photo and tried again and all works fine!!
Comment #54
dunebl#49 doesn't apply cleanly on 9.1
Comment #55
mparker17Here's the patch in #49, re-rolled. There are no changes, so no interdiff.
Comment #56
ranjith_kumar_k_u commentedI have tested the last patch on 9.2 dev version,the patch applied cleanly but it does not resolve issue

Comment #57
Mike.Brawley commented#55 worked on 8.9.12
Comment #58
webdrips commented#55 did not work for me, but it seems to be a step in the right direction.
I am now able to view the default image after uploading it, but it does not display as a default using the layout builder.
Tested on 8.9.13
Comment #59
bas123 commentedI ran into this problem again after fixing it several months ago with several Open Social and Drupal updates in between.
Currently running Drupal 8.9.13 and Open Social 8.x-9.9 thru 10.0.3
So I ran the patch again:
git apply -v private-default-image-is-inaccessible-2107455-24.patch(Kept in in my /html/ [root] folder) and it resolved the problem returning the default image in every place it is supposed to appear, including Activity Streams and anywhere where the user has not uploaded a replacement!I am assuming then that since this issue is still in review, that it has not made it's way to the Drupal code/update process and thus may need to be reviewed each time an update is done.
Comment #60
System Lord commented#55 worked for me (manual patch). No errors in watchdog or browser inspector. Path/folders are correct and all styles work. Thank you.
D9.1.5
PHP 7.4.16
Apache - not running Nginx. (May be relevant to this issue?: https://www.drupal.org/forum/support/post-installation/2021-03-29/does-a... )
Comment #62
bas123 commentedI would just like to reiterate that until this issue is resolved within Core, the patch does work.
The problem is that every time I do a core update, it is necessary to run:
git apply -v private-default-image-is-inaccessible-2107455-24.patchwhich solves the problem of missing default profile photos.Comment #63
kapilv commentedHear a reroll patch.
Comment #64
kapilv commentedComment #66
kapilv commentedComment #68
bas123 commentedReferencing #62
Drupal 8.9.20 with Open Social (social-10.3.8)
git apply -v private-default-image-is-inaccessible-2107455-24.patchRenders:
I am wondering if this has any relationship with: https://www.drupal.org/project/social/issues/3261346
When Logged Out (Stream):

When Logged Out (About Us page):

When Logged in (About Us page:

Comment #69
socialnicheguru commentedNeither of the last two patches apply to Drupal core 9.2.12
Comment #70
ravi.shankar commentedAdded reroll of patch #63 on Drupal 9.2.x.
Comment #71
grabby commentedI apologize if I’m posting in the wrong thread, but on 9.3.9 if I set the file system to private and then upload an image to the core image field (field_image) attached to Article and Basic page it gets saved to the public (sites/default/files/images) system. All other image fields get saved in the private system as desired.
Comment #72
yogeshmpawarUpdated patch will fix test failure. removed decprecated code.
Comment #74
andileco commentedThis #72 didn't apply for me on 9.4.1, so rerolled.
Comment #75
pooja saraah commentedFixed failed commands #74
Attached patch against 9.5.x
Comment #77
philyPatch #75 tested with success against Drupal 9.4.8
Thanks.
Comment #78
luenemann$file can be null with imported field_config on a different site where there's no file with that uuid.
In that case you get
Error: Call to a member function getFileUri() on nullfor every default image download.
Should wrap it with
if (isset($file)) {.Comment #79
ravi.shankar commentedAdded reroll of patch #74 on Drupal 10.1.x and made changes as per comment #78.
Comment #80
rinku jacob 13 commentedSuccessfully applied patch #79 for drupal version 10.1.x. Adding Screenshots for the references
Comment #81
joseph.olstadJustification:
Patch has real world success as per #80.
Patch has test coverage.
Patch passes tests.
Fix is needed.
Comment #82
xjmThis is quite a lot of procedural code, plus the
&drupal_static()in there. This looks like D7 code. Is there a reason it has to be added toimage.modulerather than being better encapsulated?Tagging for framework manager feedback on the architecture. This could also use @claudiu.cristea's feedback as the subsystem maintainer -- their patch was the original prototype back in 2013, but the codebase has changed quite a lot since then.
Comment #83
alexpottCan use
str_starts_with()here.I agree with @xjm - this code should be a service and the if we do have a cache then it'll need to be cleared when a field is changed.
Comment #84
jan kellermann commentedReworks patch #74 for Drupal 9.4.13
Comment #86
xjmHiding the D9.4 patch; we don't need that. We need #82 and #83 addressed. Thanks!
Comment #87
xjmComment #88
bas123 commentedI just updated to Social (social-11.8.12) and the ugly Default Profile image wouldn't display once again and I couldn't get any of the patches to work until I tried #84 (2107455-84.patch)
At least for now that one solved it in four Drupal Open Social websites! so Kudos to Jan Kellermann!
I just wish this problem which has persisted for several years and incarnations would ge committed to core...
Comment #89
metalboteJust reroll for 10.1.6
Comment #91
yospyn commentedUpgrading to core 10.2.1 caused #79 (and #89) to fail to apply.
I was using #79 successfully with 10.1.7.
Comment #94
eiriksmHere is a patch for 10.2
Going to try to take a swing at the feedback in #82 and #83
Comment #97
eiriksmOpened a MR (against 11.x) where i added back the test module needed to actually run the tests in the patch.
Also refactored the procedural style code into a service.
I'm pretty sure we are missing a bit of test coverage on this one though (for example the code branch concerning "A default image could be used by multiple field configs."), but since it's a bug it might not be the main priority. The test certainly demonstrates the bug at least, so that's the most important part for me.
Anyway. the feedback in #82 and #83 should be addressed in this MR
Comment #98
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue.
While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)
Comment #99
yospyn commentedPatch #94 via eiriksm worked for me on 10.2.3, many thanks.
Comment #100
eiriksmGuess it should have status needs review?
Comment #101
smustgrave commentedAssuming the MR, appears to have test failures.
Comment #102
yospyn commentedPatch #94 not working with 10.3, so sticking to 10.2.7 for sites involving this.
Comment #103
joseph.olstad@yospyn , are there any watchdog /php log messages or helpful console exceptions that provide some insight?
Comment #104
joseph.olstad@yospyn,
The MR was updated, it now rolls with fuzz on 10.3.x, might work for you.
This patch:
Might work with 10.3.x
Comment #105
joseph.olstadw00t! All passing as a service now.
Comment #106
luenemannTest only fails as expected: https://git.drupalcode.org/issue/drupal-2107455/-/pipelines/210442/test_...
Comment #107
joseph.olstadRationale for RTBC:
Comment #108
joseph.olstad@kksandr , I'll leave the other suggested changes for others:
@todo:
Comment #109
joseph.olstadReverted 5158f6f6, it didn't seem right to change the storage from field_config to entity_field.manager as suggested by @kksandr, so backing it off. We'll see if this greens it up. ***BEGIN EDIT*** it did light up green after this revert ***END EDIT***
Comment #110
kksandr commentedComment #111
kksandr commentedComment #114
kksandr commentedChanges that I made:
entity_field.managerto get fields instead of loading only configuration fields.AccessResultInterface, which makes it possible to provide cache metadata if it is to be used somewhere other than a file access check.I also left one detail tagged
@todofor review.Comment #115
kksandr commentedComment #116
kksandr commentedI moved the new service to the images module because it handles loading default images and checking access to them.
Comment #117
yospyn commented@joseph.olstad thanks for working on this, when I apply the patch in #104 I get this error:
ParseError: syntax error, unexpected identifier "DEFAULT_IMAGE_DIRECTORY", expecting "=" in Composer\Autoload\{closure}() (line 17 of /app/web/core/modules/image/src/ImageFieldManagerInterface.php).Comment #118
kksandr commented@yospyn this fix targets the 11.x branch, which depends on PHP 8.3, so I used typed constants that are available in PHP 8.3. You can open a separate merge request for 10.3.x and lower the PHP requirements. You can also always update PHP on your server.
Comment #119
yospyn commented@kksandr success! Patch #104 worked with PHP 8.3 on 10.3. No errors in logs. Thank you
Comment #122
viniciusrp commentedI created a merge request for Drupal 10.3.x version and I fixed the PHP8.1 compatibility.
I cherry-picked the commits and keep the credits and in the end I add a commit to fix PHP compatibility.
Comment #123
claudiu.cristeaI have some remarks. See the MR
Comment #126
luenemannJust rebased onto 55bab340, just before the commit of #3483599: Convert all procedural hook implementations to Hook classes.
I removed some out of scope comment changes to simplify the rebase.
Comment #127
luenemannCI pipline is green, Test-only changes fails as expected.
Still "Needs work" for #123 and hook conversion.
Comment #128
kksandr commentedComment #131
dhavalpanchal commentedAdding the patch file instead of using git MR in composer json.
Comment #132
luenemannHooks in test modules should also be converted to OOP.
IMHO that is the only thing that's left.
Comment #133
luenemannThere is no more subsystem maintainer for image.module since #3501059: Remove claudiu.cristea from MAINTAINERS.txt. How to proceed?
Comment #134
kksandr commentedComment #135
carstenhager commentedThanks for your work. It works for me too for 10.4
Comment #136
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #137
kksandr commentedComment #138
joseph.olstad@kksandr, thanks for fixing the merge request!
Comment #140
joseph.olstadI created a new merge request because the branch name of the merge request was 11.x which is a confusing sort of branch name as it matches the branch name in origin. Perhaps was an accidental choice. In any case, I suggest moving forward with an appropriately named branch.
I also triggered a tests only pipeline. Reminder that a fail for a tests only run is success provided that the normal pipeline is green!
Comment #143
kksandr commentedComment #144
joseph.olstadI believe this is ready for a subsystem maintainer review.
Comment #145
godotislateA couple comments on the MR. I think there might be missing test coverage for a private default image set on field config instead of field storage config.
Comment #146
godotislateFunny enough, the order of operations between
&&and=was also the problem behind #3403999: Exposed filter values ignored when using batch.Comment #147
kksandr commented@godotislate These are two completely different cases.
Comment #148
kksandr commentedComment #149
godotislateLGTM.
Removing Needs Tests and Needs subsystem maintainer review since it looks like both were already done.
Comment #150
catchThis could use an issue summary update (well, any issue summary at all), given there are 150 comments and a couple of hundred lines of changes on the MR.
Comment #151
luenemannIssue summary was removed in #2107455-131: Image field default value not shown when upload destination set to private file storage. I am going to restore the previous state.
Comment #152
luenemannComment #153
luenemannComment #154
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #155
luenemannFix long comment lines
Comment #157
yospyn commentedCurious about patch availability for core 11.1.7, tried #131 and the .diff above but no success.
Comment #158
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #159
luenemannRebased and resolved merge conflict.
I am going to RTBC, because my changes are only fixing comment coding standard.
Comment #160
quietone commentedThere are several comments that are not wrapped correctly. I left suggestions for those in the MR. I didn't apply them because longwave left questions that need to be answered. Therefor, setting to needs work.
Also, I updated credit which is always challenging when there are lots of comments and contributors.
Comment #162
kksandr commentedComment #163
yospyn commentedThe diff above worked for me when upgrading to core 11.3.2. Have a couple private intranets with user profiles that use the default image, which was previously broken but now appears OK.
Comment #164
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #165
godotislateComment #166
dhavalpanchal commentedAdding the changes as a patch file which is implemented in 11327 merge request the changes are working on Drupal 11.3 correctly.
Comment #167
kksandr commentedComment #168
smustgrave commentedTest only run shows the coverage https://git.drupalcode.org/issue/drupal-2107455/-/jobs/9055205
Think I'm not 100% sure on is the #Hook() applied to the entire class? Let me ask nicxvan
Comment #169
nicxvan commentedJust to clarify my comment on the MR and the above, there is nothing wrong with Hook attributes on the class, it's fully supported.
I think in core honestly we should prefer consistency and have them on the method so the hook being implemented is immediately apparent when looking at the method.
Comment #170
smustgrave commentedThis one needs a rebase but going to try and make the call can we add the hook to the method to be consistent as mentioned please.
Thanks all!