Skip to content

Commit 0c9fabb

Browse files
authored
Merge pull request #6108 from BookStackApp/view_revisions_permission
Permissions: Started addition of revision-view permission
2 parents 083fb1a + 426f9ac commit 0c9fabb

11 files changed

Lines changed: 152 additions & 6 deletions

File tree

app/Entities/Controllers/PageRevisionController.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function __construct(
3434
*/
3535
public function index(Request $request, string $bookSlug, string $pageSlug)
3636
{
37+
$this->checkPermission(Permission::RevisionViewAll);
3738
$page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
3839
$listOptions = SimpleListOptions::fromRequest($request, 'page_revisions', true)->withSortOptions([
3940
'id' => trans('entities.pages_revisions_sort_number')
@@ -65,6 +66,8 @@ public function index(Request $request, string $bookSlug, string $pageSlug)
6566
*/
6667
public function show(string $bookSlug, string $pageSlug, int $revisionId)
6768
{
69+
$this->checkPermission(Permission::RevisionViewAll);
70+
6871
$page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
6972
/** @var ?PageRevision $revision */
7073
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
@@ -94,6 +97,8 @@ public function show(string $bookSlug, string $pageSlug, int $revisionId)
9497
*/
9598
public function changes(string $bookSlug, string $pageSlug, int $revisionId)
9699
{
100+
$this->checkPermission(Permission::RevisionViewAll);
101+
97102
$page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
98103
/** @var ?PageRevision $revision */
99104
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
@@ -129,6 +134,7 @@ public function changes(string $bookSlug, string $pageSlug, int $revisionId)
129134
*/
130135
public function restore(string $bookSlug, string $pageSlug, int $revisionId)
131136
{
137+
$this->checkPermission(Permission::RevisionViewAll);
132138
$page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
133139
$this->checkOwnablePermission(Permission::PageUpdate, $page);
134140

@@ -144,6 +150,7 @@ public function restore(string $bookSlug, string $pageSlug, int $revisionId)
144150
*/
145151
public function destroy(string $bookSlug, string $pageSlug, int $revId)
146152
{
153+
$this->checkPermission(Permission::RevisionViewAll);
147154
$page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
148155
$this->checkOwnablePermission(Permission::PageDelete, $page);
149156

app/Permissions/Permission.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ enum Permission: string
118118
case PageViewAll = 'page-view-all';
119119
case PageViewOwn = 'page-view-own';
120120

121+
case RevisionViewAll = 'revision-view-all';
122+
121123
/**
122124
* Get the generic permissions which may be queried for entities.
123125
*/
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
use Carbon\Carbon;
4+
use Illuminate\Database\Migrations\Migration;
5+
6+
return new class extends Migration
7+
{
8+
/**
9+
* Run the migrations.
10+
*/
11+
public function up(): void
12+
{
13+
// Create new revision-view-all permission
14+
$permissionId = DB::table('role_permissions')->insertGetId([
15+
'name' => 'revision-view-all',
16+
'created_at' => Carbon::now()->toDateTimeString(),
17+
'updated_at' => Carbon::now()->toDateTimeString(),
18+
]);
19+
20+
// Get ids of page view permissions
21+
$pageViewPermissions = DB::table('role_permissions')
22+
->whereIn('name', [
23+
'page-view-own',
24+
'page-view-all',
25+
])->get();
26+
27+
if ($pageViewPermissions->count() === 0) {
28+
return;
29+
}
30+
31+
// Get role ids which have page view permission
32+
$applicableRoleIds = DB::table('permission_role')
33+
->whereIn('permission_id', $pageViewPermissions->pluck('id'))
34+
->pluck('role_id')
35+
->unique()
36+
->all();
37+
38+
// Assign the new permission to relevant roles
39+
$newPermissionRoles = array_values(array_map(function (int $roleId) use ($permissionId) {
40+
return [
41+
'role_id' => $roleId,
42+
'permission_id' => $permissionId,
43+
];
44+
}, $applicableRoleIds));
45+
46+
DB::table('permission_role')->insert($newPermissionRoles);
47+
}
48+
49+
/**
50+
* Reverse the migrations.
51+
*/
52+
public function down(): void
53+
{
54+
// Get the permission to remove
55+
$revisionViewPermission = DB::table('role_permissions')
56+
->where('name', '=', 'revision-view-all')
57+
->first();
58+
59+
if (!$revisionViewPermission) {
60+
return;
61+
}
62+
63+
// Remove the permission, and its use on roles, from the database
64+
DB::table('permission_role')->where('permission_id', '=', $revisionViewPermission->id)->delete();
65+
DB::table('role_permissions')->where('id', '=', $revisionViewPermission->id)->delete();
66+
}
67+
};

lang/en/settings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
'role_all' => 'All',
208208
'role_own' => 'Own',
209209
'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
210+
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
210211
'role_save' => 'Save Role',
211212
'role_users' => 'Users in this role',
212213
'role_users_none' => 'No users are currently assigned to this role',

resources/views/entities/meta.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</div>
1010
@endif
1111

12-
@if ($entity->isA('page'))
12+
@if ($entity->isA('page') && userCan(\BookStack\Permissions\Permission::RevisionViewAll))
1313
<a href="{{ $entity->getUrl('/revisions') }}" class="entity-meta-item">
1414
@icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }}
1515
</a>

resources/views/exports/parts/meta.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="entity-meta">
2-
@if ($entity->isA('page'))
2+
@if ($entity->isA('page') && userCan(\BookStack\Permissions\Permission::RevisionViewAll))
33
@icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br>
44
@endif
55

resources/views/pages/parts/show-sidebar-section-actions.blade.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
</a>
2525
@endif
2626
@endif
27-
<a href="{{ $page->getUrl('/revisions') }}" data-shortcut="revisions" class="icon-list-item">
28-
<span>@icon('history')</span>
29-
<span>{{ trans('entities.revisions') }}</span>
30-
</a>
27+
@if(userCan(\BookStack\Permissions\Permission::RevisionViewAll))
28+
<a href="{{ $page->getUrl('/revisions') }}" data-shortcut="revisions" class="icon-list-item">
29+
<span>@icon('history')</span>
30+
<span>{{ trans('entities.revisions') }}</span>
31+
</a>
32+
@endif
3133
@if(userCan(\BookStack\Permissions\Permission::RestrictionsManage, $page))
3234
<a href="{{ $page->getUrl('/permissions') }}" data-shortcut="permissions" class="icon-list-item">
3335
<span>@icon('lock')</span>

resources/views/settings/roles/parts/form.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class="item-list toggle-switch-list">
7979
@include('settings.roles.parts.asset-permissions-row', ['title' => trans('entities.books'), 'permissionPrefix' => 'book'])
8080
@include('settings.roles.parts.asset-permissions-row', ['title' => trans('entities.chapters'), 'permissionPrefix' => 'chapter'])
8181
@include('settings.roles.parts.asset-permissions-row', ['title' => trans('entities.pages'), 'permissionPrefix' => 'page'])
82+
@include('settings.roles.parts.revisions-permissions-row', ['title' => trans('entities.revisions'), 'permissionPrefix' => 'revision'])
8283
@include('settings.roles.parts.related-asset-permissions-row', ['title' => trans('entities.images'), 'permissionPrefix' => 'image'])
8384
@include('settings.roles.parts.related-asset-permissions-row', ['title' => trans('entities.attachments'), 'permissionPrefix' => 'attachment'])
8485
@include('settings.roles.parts.related-asset-permissions-row', ['title' => trans('entities.comments'), 'permissionPrefix' => 'comment'])
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<div class="item-list-row flex-container-row items-center wrap">
2+
<div class="flex py-s px-m min-width-s">
3+
<strong>{{ $title }}</strong> <br>
4+
<a href="#" refs="permissions-table@toggle-row" class="text-small text-link">{{ trans('common.toggle_all') }}</a>
5+
</div>
6+
<div class="flex py-s px-m min-width-xxs">
7+
<small class="hide-over-m bold">{{ trans('common.create') }}<br></small>
8+
<strong class="text-muted opacity-70 text-large">-</strong>
9+
</div>
10+
<div class="flex py-s px-m min-width-xxs">
11+
<small class="hide-over-m bold">{{ trans('common.view') }}<br></small>
12+
@include('settings.roles.parts.checkbox', ['permission' => $permissionPrefix . '-view-all', 'label' => trans('settings.role_all')])
13+
</div>
14+
<div class="flex py-s px-m min-width-xxs">
15+
<small class="hide-over-m bold">{{ trans('common.edit') }}<br></small>
16+
<strong class="text-muted opacity-70 text-large">-</strong>
17+
</div>
18+
<div class="flex py-s px-m min-width-xxs">
19+
<small class="hide-over-m bold">{{ trans('common.delete') }}<br></small>
20+
<small>{{ trans('settings.role_controlled_by_page_delete') }}</small>
21+
</div>
22+
</div>

tests/Entity/PageRevisionTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use BookStack\Activity\ActivityType;
66
use BookStack\Entities\Models\Page;
7+
use BookStack\Entities\Models\PageRevision;
8+
use BookStack\Permissions\Permission;
79
use Tests\TestCase;
810

911
class PageRevisionTest extends TestCase
@@ -257,6 +259,33 @@ public function test_revision_changes_view_filters_html_content()
257259
$revisionView->assertDontSee('dontwantthishere');
258260
}
259261

262+
public function test_access_to_revision_operation_requires_revision_view_all_permission()
263+
{
264+
$editor = $this->users->editor();
265+
$this->actingAs($editor);
266+
267+
$page = $this->entities->page();
268+
$this->createRevisions($page, 3);
269+
/** @var PageRevision $revision */
270+
$revision = $page->revisions()->orderBy('id', 'desc')->first();
271+
272+
$this->get($page->getUrl())->assertSee($page->getUrl('/revisions'), false);
273+
$this->get($page->getUrl('/revisions'))->assertOk();
274+
$this->get($revision->getUrl())->assertOk();
275+
$this->get($revision->getUrl('/changes'))->assertOk();
276+
$this->put($revision->getUrl('/restore'))->assertRedirect($page->getUrl());
277+
$this->delete($revision->getUrl('/delete'))->assertRedirect($page->getUrl('/revisions'));
278+
279+
$this->permissions->removeUserRolePermissions($editor, [Permission::RevisionViewAll]);
280+
281+
$this->get($page->getUrl())->assertDontSee($page->getUrl('/revisions'), false);
282+
$this->assertPermissionError($this->get($page->getUrl('/revisions')));
283+
$this->assertPermissionError($this->get($revision->getUrl()));
284+
$this->assertPermissionError($this->get($revision->getUrl('/changes')));
285+
$this->assertPermissionError($this->put($revision->getUrl('/restore')));
286+
$this->assertPermissionError($this->delete($revision->getUrl('/delete')));
287+
}
288+
260289
public function test_revision_restore_action_only_visible_with_permission()
261290
{
262291
$page = $this->entities->page();

0 commit comments

Comments
 (0)