Problem/Motivation
When you upload a media item it is by default available at /media/{id} eg. media/1. Any user with "view media" will be able to access it. People can enumerate URLs to discover yet to be published content. For example, if you have content_moderation enabled and you upload media to a draft content entity the media will be automatically published to /media/{id}. If the content is a timed press release this could cause a lot of problems.
Regardless of the access issue, I think it is surprising that uploading media to a content entity makes the media available at another URL. This does not feel like the 80% use-case and it makes the access problems described by #2904842: Make private file access handling respect the full entity reference chain worse.
Proposed resolution
- Make the edit-form the
canonicalroute (andrevision) route for media entities. (This is the same as BlockContent entities) - To retain BC: Add a tick box to media settings to restore removed routes
Remaining tasks
None.
User interface changes
/media/{id}is not available to users with the permission "view media" by default.- A new option on /admin/config/media/media-settings to ensure access to media items at
/media/{id}
API changes
The default canonical route for media items is the edit-form and access to it is the same as edit access to media unless the media.settings:security.standalone_url is set to true
Data model changes
None
Release notes snippet
Media items are no longer available at /media/{id} by default. This can be enabled on /admin/config/media/media-settings.
| Comment | File | Size | Author |
|---|---|---|---|
| #43 | 3017935-39.patch | 39.72 KB | wim leers |
| #42 | 3017935-11.patch | 14.33 KB | wim leers |
| #42 | interdiff.txt | 9.41 KB | wim leers |
| #38 | caga_tio.jpg | 528.82 KB | chr.fritsch |
| #32 | interdiff-3017935-30-32.txt | 2.42 KB | chr.fritsch |
Comments
Comment #2
chr.fritschHere is a first patch. There are a couple of things that should be adjusted:
* We need an update path to set the checkbox for existing sites.
* Changing the setting, doesn't clear the entity type cache. Not sure about this.
* Some documentation in the settings form why it is risky to expose the canonical URL.
Finally, let's see how much tests are failing.
Comment #3
chr.fritschComment #4
wim leersLet's prefix this withbc_, just like we did in #2751325: All serialized values are strings, should be integers/booleans when appropriate.@chr.fritsch pointed out that this is not actually solely for BC, some sites may have an explicit need for this.
Why did we move these?
We lost the weight here.
This means that a config change needs to invalidate entity type definitions, i.e. the
entity_typescache tag.It also means routes need to be rebuilt, so you'll want to invalidate the
routescache tag too AFAICT.See
\Drupal\rest\EventSubscriber\RestConfigSubscriberand\Drupal\system\EventSubscriber\ConfigCacheTag::onSave()an example.Let's put in an
@seeto the configuration here, and explain why we even need this.I suggest s/expose_canonical/expose_canonical_routes/ and "Make media items accessible via their own pages"
Übernit: s/an/a/
Comment #5
wim leersSuggestion:
By default, published !media-entities are only accessible to end users if they're shown (referenced) by other entities. On most sites, access to individual !media-entities (via guessable URLs) is unexpected. If it makes sense for your site to have all !media-entities be individually accessible, turn this setting on.(For
!media-entities, uselabel_pluralfrom the entity type definition.)That's quite verbose and perhaps a bit scary, but I think that's warranted in this case.
Comment #7
chr.fritschRegarding #4
2. Because we have to set a different parent
3. Fixed
4. We have still have the problem that
media_entity_type_alteris called afterDefaultHtmlRouteProvider::getRoutes()5. Fixed
6. Fixed
7. Fixed
Also added the suggestion from #5
Comment #9
wim leersWhy this weight? Let's document the reason.
Übernit: the last line needs an extra 2 spaces of leading indentation.
Nit: s/URL's/URLs/
🤔 Hm, this could be tricky. This means that the URL that is returned depends on the user (which permissions do they have?) and the configuration (which permissions does each role have?).
I'd say that we should merge the cacheability of the access result with the resulting
Urlobject, but … the\Drupal\Core\Urlobject does not carry cacheability.\Drupal\Core\GeneratedUrldoes … but that's not what this method returns.Do we really need to conditionally return different routes?
s/router/route/
(And yes,
\Drupal\rest\EventSubscriber\RestConfigSubscribergot this wrong too 😅)C/P leftover :)
Nit: entity type manager.
Comment #10
chr.fritschThis patch fixes the caching issue we had when changing the setting.
It also adds a super-simple update path to set the setting for existing sites.
I will now start to address #9
Comment #11
wim leersKeeps canonical routes for Media entities enabled on existing sites.
entity type manager
✅ Needs a comment. Added that. Explanation is similar to #2241275: DateFormat cache tag: don't set the cache tag, but invalidate the entire render cache?.
These would guarantee the correct order, but why was this not necessary before, but is now?
Comment #12
chr.fritschThis fixes #9 and #11
Comment #13
wim leersAll HAL REST
GETtests are failing (Testing Drupal\Tests\media\Functional\Hal\MediaHalJsonAnonTestand friends), even after adding something likeIt's then failing like this:
That's triggered by this line:
Manually stepping through this confirms this seemingly impossible thing, that loading something by ID succeeds and simply calling
disable()->save()somehow causes the loaded config entity to have a UUID that differs from the original config entity. Calling$this->resourceConfigStorage->resetCache([static::$resourceConfigId]);doesn't help.If this is not scary enough, what's worse is that this happens only for HAL tests. Why? How? I have no clue. I already spent about 45 minutes debugging this. I'm moving on to something else.
Comment #14
chr.fritschI changed the implementation to stay with the canonical link template. Now we are setting the canonical URL to the edit URL.
Let's see how many tests will fail now.
Comment #15
andypostComment #17
chr.fritschSo let's start fixing tests.
BTW: I don't think that the related issue is affecting us, because our route has no optional arguments.
Comment #19
wim leers🎉😭🙈We spent so much time debugging this! Great find :)
Comment #20
chr.fritschThis should fix the remaining test fails
Comment #21
berdir> We spent so much time debugging this! Great find :)
That's exactly why I think that using $this->container and properties with services is a bad idea in tests, should either just use \Drupal::() or alternatively methods. See #2066993: Use magic methods to sync container property to \Drupal::getContainer in functional tests.
Comment #22
tim.plunkettAfter reading the title, I came here to suggest this (the same as BlockContent) instead of actually removing it. +1!
Comment #23
alexpottThis setting name needs work. It's super tricky because you don't want to expose too much of the internals. But I think this setting should be about using the edit-form as the canonical route instead of the "view" (nice overloaded word in Drupal). The current description also has a lot of assumptions baked in. The concept of "end-users" might include editors who would have access to edit the media entity.
The rebuildAll() makes me wonder if we've got this right. Does it work if we only force a route rebuild in tests?
I think this is a slightly confusing construction. @todo...
Is the clearCachedDefinitions() needed after a rebuildAll()?
Comment #24
chr.fritsch1. I talked to @alexpott and @Wim Leers and we decided to rename the setting to standalone_media_url
2. Nice catch,
$this->container->get('router.builder')->rebuild();is enough for us to work3. Added a comment
4. Actually it's not needed.
Comment #25
phenaproximaAwesome work. I think this patch is an excellent fix for a pretty serious WTF in the media system. One request, though:
Having the word "media" in this setting name is redundant. Can we just call this standalone_url? And can the description be changed to "Whether media items can be accessed at standalone URLs"?
Comment #26
alexpottAllow media entities to be viewed at media/IDLet's explain it here.
This needs a rename. enable_standalone_url?
I think this can be improved. Maybe
Listens to the config save event for media.settings.Does more than this.
Either
eventorConfigCrudEventI think the second sentence is unnecessary.
Provide an edit_form task if standalone media URLs are enabled.The config factory can be injected. Override __construct and createInstance
And these... one
Can these be replaced with a router rebuild too?
Comment #27
chr.fritschI addressed #25 and #26
Comment #28
alexpottThe standard for update functions is different since this is text used in the UI. See https://www.drupal.org/docs/develop/standards/api-documentation-and-comm... (that text needs to be updated for post updates) - so something like
Keep media entities viewable at media/{id}.seems right.This needs a test.
I wonder if this is necessary now?
Comment #29
chr.fritschAddressing #28
1. Changed
2. Added an update test.
3. I removed it. Let the testbot answer that question :)
Comment #30
chr.fritschThis patch needed some test changes after #2882473: Hide the media name basefield from the entity form by default landed.
Comment #31
alexpottThese changes are a little odd. Maybe we should remove the drupalGets? After all they are a little pointless now and we have other assertions checking that the media has been uploaded / changed etc...
Comment #32
chr.fritschLet's kick them.
Comment #33
alexpottDiscussion at the Barcelona sprint has resulted in the decision that this needs Caga tió.
Comment #34
chr.fritsch👍🏻
Comment #35
wim leers👍
Comment #36
seanb#33 +1
Comment #37
phenaproximaAgreed, +1!
Comment #38
chr.fritschOk, here is the Caga Tio. Let's proceed 😁
Comment #39
alexpottComment #40
alexpottAdded a change record - https://www.drupal.org/node/3018742
And fixed the issue title.
Comment #41
alexpottComment #42
wim leersOnly found nits (see below), and fixed them all. Let's get this done.
Allow media items to be viewed standalone at /media/{id}.
Nit: Missing leading slash.
s/entities/items/
s/router/route/
Nit.
The first line is kinda pointless. The second is great. Let's make it fit on a single line.
Nit.
s/service//
s/on/at/
s/media//media/
s/media/media-related/
s/service//
Should not use camelCase.
Comment #43
wim leersUgh, right interdiff, wrong patch.
Comment #45
wim leersCR reviewed; added some small clarifications.
Comment #46
alexpottCommitted a6e4eaf and pushed to 8.7.x. Thanks!
Small fix on commit.
Comment #49
berdirWhy was this done a as a post update, this is just a regular plain config save?
It being a post update makes it impossible for me to also set that to FALSE in existing installations of my installation profile.
IMHO, we should either change it to a regular update function or check it against NULL, so that it is only set if NULL. Then I can do my own update or post update function and the execution order doesn't matter, mine will win.
Comment #50
berdirCreated #3036787: media_post_update_enable_standalone_url() should only set TRUE if not already set.
Comment #51
berdirNote: This breaks linkit media integration as that no longer exposes a matcher plugin for media entities. Created an issue there #3040749: Drupal 8.7 no longer has a dedicated canonical route for media entities, breaks entity matcher deriver.
Comment #52
whiklojIs it a longer term plan to remove all ability to access the fields and content of media outside of nodes? We have been building a semantic data repository on Drupal 8 and testing the move from 8.6.10 to 8.7.x had me worried. I see the canonical route for media entities can be re-enabled. But I am just wondering what the longer term plan for media entities is?
Comment #53
berdirI don't think there are plans beyond this issue. Most sites don't need/want them to be exposed, but you can still chose to do so if that makes sense for you.
Comment #54
whiklojThanks for your quick response, I also noticed that the media REST endpoint seems to have been changed as well from the old
media/{id}tomedia/{id}/edit. I don't think it is from this ticket. but I might have missed that change or is that a completely separate ticket?Comment #55
berdirThat was most likely caused by this and probably not something that we expected to happen. Not seeing any test changes related to that, so I think our test coverage is a bit too generic to catch that :-/. I'd suggest you open an new issue (bug?) about that. A bit tricky now, because changing it again could be seen as yet another change.
Comment #56
wim leersWow, great catch, @whikloj! And yes, @Berdir is right, this is a consequence of our REST test coverage being generic. OTOH it's pretty exceptional to have
foo/{…}/editas acanonicalroute, so I'm not too concerned about the REST test coverage.The fix should be easy, could you file that issue that @Berdir asked for, @whikloj? 🙏
Comment #57
whikloj@Berdir @Wim Leers... cancel that. It was a PEBKAC error, I thought I had cleared my cache but apparently I had not.
**edit**
To clarify the REST route does change to
media/{id}/editdue to this change, but if you check the checkbox in Media Settings it reverts tomedia/{id}.Comment #58
chris matthews commentedIt took me a minute to discover this checkbox was at /admin/config/media/media-settings as I didn't really think of it as a "security setting". I know this issue is fixed, but does anyone else think it might have a better home on the edit page for each media type (i.e. at /admin/structure/media/manage/audio > Publishing options) so there is more fine grained control per media type?
Comment #59
andrewmacpherson commentedRe. #58 - can you file a new issue to propose this? I think it's a good idea, though it'll need discussion. It sounds similar to what the Rabbit Hole contrib module does.
Comment #60
chris matthews commented@andrewmacpherson As suggested: #3081741: Allow media to expose a standalone URL on a per-bundle basis
Comment #61
wim leersWe overlooked the path alias: #3067944: Follow-up for #3017935: Media entities should no longer have path alias support by default.