Skip to content

Commit b9467f5

Browse files
duncanmccleanclaude
andcommitted
Fix entry revision localizations to filter unauthorized sites
The revision preview endpoint was exposing all collection sites in the localizations array regardless of user permissions. This allowed users with restricted site access to view unauthorized site details in the API response. Apply the same authorization filtering used in EntriesController by adding getAuthorizedSitesForCollection() to filter sites based on the current user's view permissions. Fixes #14697 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a2b0a0f commit b9467f5

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

src/Http/Controllers/CP/Collections/EntryRevisionsController.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function show(Request $request, $collection, $entry, $revision)
8383
'readOnly' => true,
8484
'published' => $entry->published(),
8585
'locale' => $entry->locale(),
86-
'localizations' => $entry->collection()->sites()->map(function ($handle) use ($entry) {
86+
'localizations' => $this->getAuthorizedSitesForCollection($entry->collection())->map(function ($handle) use ($entry) {
8787
$localized = $entry->in($handle);
8888
$exists = $localized !== null;
8989

@@ -120,4 +120,11 @@ protected function collectionToArray($collection)
120120
'url' => cp_route('collections.show', $collection->handle()),
121121
];
122122
}
123+
124+
protected function getAuthorizedSitesForCollection($collection)
125+
{
126+
return $collection
127+
->sites()
128+
->filter(fn ($handle) => User::current()->can('view', Site::get($handle)));
129+
}
123130
}

tests/Feature/Entries/EntryRevisionsTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,57 @@ public function localized_entry_with_localizable_date_keeps_its_own_date_when_re
699699
);
700700
}
701701

702+
#[Test]
703+
public function revision_localizations_only_includes_authorized_sites()
704+
{
705+
$this->setSites([
706+
'en' => ['url' => 'http://localhost/', 'locale' => 'en'],
707+
'fr' => ['url' => 'http://localhost/fr/', 'locale' => 'fr'],
708+
'de' => ['url' => 'http://localhost/de/', 'locale' => 'de'],
709+
]);
710+
711+
$this->setTestBlueprint('test', ['foo' => ['type' => 'text']]);
712+
$this->setTestRoles(['test' => [
713+
'access cp',
714+
'view blog entries',
715+
'access en site',
716+
'access fr site',
717+
// Note: no 'access de site' permission
718+
]]);
719+
$user = User::make()->id('user-1')->assignRole('test')->save();
720+
721+
$this->collection->sites(['en', 'fr', 'de'])->save();
722+
723+
$entry = EntryFactory::id('1')
724+
->slug('test')
725+
->collection('blog')
726+
->locale('en')
727+
->published(true)
728+
->date('2010-12-25')
729+
->data([
730+
'blueprint' => 'test',
731+
'title' => 'Original title',
732+
'foo' => 'bar',
733+
])->create();
734+
735+
$revision = tap($entry->makeRevision(), function ($copy) {
736+
$copy->message('Revision one');
737+
$copy->date(Carbon::parse('2017-02-01'));
738+
});
739+
$revision->save();
740+
741+
$response = $this
742+
->actingAs($user)
743+
->getJson($entry->revisionsUrl().'/'.$revision->date()->timestamp)
744+
->assertOk();
745+
746+
$localizations = $response->json('localizations');
747+
748+
// User should only see en and fr sites, not de
749+
$this->assertCount(2, $localizations);
750+
$this->assertEquals(['en', 'fr'], array_column($localizations, 'handle'));
751+
}
752+
702753
private function setTestBlueprint($handle, $fields)
703754
{
704755
$blueprint = Blueprint::makeFromFields($fields)->setHandle($handle);

0 commit comments

Comments
 (0)