From da019136166fdf69fec526d8a056411bf8b3478d Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 19 Dec 2021 12:27:14 +0000 Subject: [PATCH] Added ability to copy a role - Copies via loading in model on create view. - Updated role views while editing to bring up to similar format as that used for more modern app areas. - Added tests to cover. Related to #1123 --- app/Http/Controllers/RoleController.php | 19 +- .../views/settings/roles/create.blade.php | 18 +- resources/views/settings/roles/edit.blade.php | 51 +- .../views/settings/roles/parts/form.blade.php | 479 ++++++++---------- tests/Permissions/RolesTest.php | 17 + 5 files changed, 312 insertions(+), 272 deletions(-) diff --git a/app/Http/Controllers/RoleController.php b/app/Http/Controllers/RoleController.php index 25b2a5851..7c72b5970 100644 --- a/app/Http/Controllers/RoleController.php +++ b/app/Http/Controllers/RoleController.php @@ -3,6 +3,7 @@ namespace BookStack\Http\Controllers; use BookStack\Auth\Permissions\PermissionsRepo; +use BookStack\Auth\Role; use BookStack\Exceptions\PermissionsException; use Exception; use Illuminate\Http\Request; @@ -34,11 +35,21 @@ class RoleController extends Controller /** * Show the form to create a new role. */ - public function create() + public function create(Request $request) { $this->checkPermission('user-roles-manage'); - return view('settings.roles.create'); + /** @var ?Role $role */ + $role = null; + if ($request->has('copy_from')) { + $role = Role::query()->find($request->get('copy_from')); + } + + if ($role) { + $role->display_name .= ' (' . trans('common.copy') . ')'; + } + + return view('settings.roles.create', ['role' => $role]); } /** @@ -49,7 +60,7 @@ class RoleController extends Controller $this->checkPermission('user-roles-manage'); $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], - 'description' => 'max:180', + 'description' => ['max:180'], ]); $this->permissionsRepo->saveNewRole($request->all()); @@ -84,7 +95,7 @@ class RoleController extends Controller $this->checkPermission('user-roles-manage'); $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], - 'description' => 'max:180', + 'description' => ['max:180'], ]); $this->permissionsRepo->updateRole($id, $request->all()); diff --git a/resources/views/settings/roles/create.blade.php b/resources/views/settings/roles/create.blade.php index f2edfa1c5..72afc60a8 100644 --- a/resources/views/settings/roles/create.blade.php +++ b/resources/views/settings/roles/create.blade.php @@ -8,9 +8,21 @@ @include('settings.parts.navbar', ['selected' => 'roles']) -
- @include('settings.roles.parts.form', ['title' => trans('settings.role_create')]) -
+
+

{{ trans('settings.role_create') }}

+ +
+ {{ csrf_field() }} + + @include('settings.roles.parts.form', ['role' => $role ?? null]) + +
+ {{ trans('common.cancel') }} + +
+
+ +
@stop diff --git a/resources/views/settings/roles/edit.blade.php b/resources/views/settings/roles/edit.blade.php index e2018d3e9..dda8db39d 100644 --- a/resources/views/settings/roles/edit.blade.php +++ b/resources/views/settings/roles/edit.blade.php @@ -7,10 +7,53 @@ @include('settings.parts.navbar', ['selected' => 'roles']) -
id}") }}" method="POST"> - - @include('settings.roles.parts.form', ['model' => $role, 'title' => trans('settings.role_edit'), 'icon' => 'edit']) -
+
+

{{ trans('settings.role_edit') }}

+ +
id}") }}" method="POST"> + {{ csrf_field() }} + {{ method_field('PUT') }} + + @include('settings.roles.parts.form', ['role' => $role]) + + +
+ +
+ + +
+

{{ trans('settings.role_users') }}

+ @if(count($role->users ?? []) > 0) +
+ @foreach($role->users as $user) +
+
+ {{ $user->name }} +
+
+ @if(userCan('users-manage') || user()->id == $user->id) + id}") }}"> + @endif + {{ $user->name }} + @if(userCan('users-manage') || user()->id == $user->id) + + @endif +
+
+ @endforeach +
+ @else +

+ {{ trans('settings.role_users_none') }} +

+ @endif +
@stop diff --git a/resources/views/settings/roles/parts/form.blade.php b/resources/views/settings/roles/parts/form.blade.php index 9cea9e1fb..a15117e5e 100644 --- a/resources/views/settings/roles/parts/form.blade.php +++ b/resources/views/settings/roles/parts/form.blade.php @@ -1,267 +1,224 @@ -{!! csrf_field() !!} - -
-

{{ $title }}

- -
- -
-
- -
-
-
- - @include('form.text', ['name' => 'display_name']) -
-
- - @include('form.text', ['name' => 'description']) -
-
- @include('form.checkbox', ['name' => 'mfa_enforced', 'label' => trans('settings.role_mfa_enforced') ]) -
- - @if(in_array(config('auth.method'), ['ldap', 'saml2', 'oidc'])) -
- - @include('form.text', ['name' => 'external_auth_id']) -
- @endif -
-
- -
- - {{ trans('common.toggle_all') }} - -
-
-
@include('settings.roles.parts.checkbox', ['permission' => 'restrictions-manage-all', 'label' => trans('settings.role_manage_entity_permissions')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'restrictions-manage-own', 'label' => trans('settings.role_manage_own_entity_permissions')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'content-export', 'label' => trans('settings.role_export_content')])
-
-
-
@include('settings.roles.parts.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'users-manage', 'label' => trans('settings.role_manage_users')])
-
@include('settings.roles.parts.checkbox', ['permission' => 'user-roles-manage', 'label' => trans('settings.role_manage_roles')])
-

{{ trans('settings.roles_system_warning') }}

-
-
-
+
+
- -

{{ trans('settings.role_asset_desc') }}

- - @if (isset($role) && $role->system_name === 'admin') -

{{ trans('settings.role_asset_admins') }}

- @endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- {{ trans('common.toggle_all') }} - {{ trans('common.create') }}{{ trans('common.view') }}{{ trans('common.edit') }}{{ trans('common.delete') }}
-
{{ trans('entities.shelves_long') }}
- {{ trans('common.toggle_all') }} -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-create-all', 'label' => trans('settings.role_all')]) - - @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-view-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-view-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.books') }}
- {{ trans('common.toggle_all') }} -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-create-all', 'label' => trans('settings.role_all')]) - - @include('settings.roles.parts.checkbox', ['permission' => 'book-view-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-view-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'book-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.chapters') }}
- {{ trans('common.toggle_all') }} -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-create-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-create-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-view-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-view-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'chapter-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.pages') }}
- {{ trans('common.toggle_all') }} -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-create-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-create-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-view-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-view-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'page-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.images') }}
- {{ trans('common.toggle_all') }} -
@include('settings.roles.parts.checkbox', ['permission' => 'image-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} - @include('settings.roles.parts.checkbox', ['permission' => 'image-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'image-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'image-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'image-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.attachments') }}
- {{ trans('common.toggle_all') }} -
@include('settings.roles.parts.checkbox', ['permission' => 'attachment-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} - @include('settings.roles.parts.checkbox', ['permission' => 'attachment-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'attachment-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'attachment-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'attachment-delete-all', 'label' => trans('settings.role_all')]) -
-
{{ trans('entities.comments') }}
- {{ trans('common.toggle_all') }} -
@include('settings.roles.parts.checkbox', ['permission' => 'comment-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} - @include('settings.roles.parts.checkbox', ['permission' => 'comment-update-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'comment-update-all', 'label' => trans('settings.role_all')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'comment-delete-own', 'label' => trans('settings.role_own')]) -
- @include('settings.roles.parts.checkbox', ['permission' => 'comment-delete-all', 'label' => trans('settings.role_all')]) -
+
-
+
+
+ + @include('form.text', ['name' => 'display_name', 'model' => $role]) +
+
+ + @include('form.text', ['name' => 'description', 'model' => $role]) +
+
+ @include('form.checkbox', ['name' => 'mfa_enforced', 'label' => trans('settings.role_mfa_enforced'), 'model' => $role ]) +
-
- {{ trans('common.cancel') }} - @if (isset($role) && $role->id) - id}") }}" class="button outline">{{ trans('settings.role_delete') }} - @endif - -
- -
- -
-

{{ trans('settings.role_users') }}

- @if(count($role->users ?? []) > 0) -
- @foreach($role->users as $user) -
-
- {{ $user->name }} -
-
- @if(userCan('users-manage') || user()->id == $user->id) - id}") }}"> - @endif - {{ $user->name }} - @if(userCan('users-manage') || user()->id == $user->id) - - @endif -
+ @if(in_array(config('auth.method'), ['ldap', 'saml2', 'oidc'])) +
+ + @include('form.text', ['name' => 'external_auth_id', 'model' => $role])
- @endforeach + @endif
- @else -

- {{ trans('settings.role_users_none') }} -

- @endif -
+
+ +
+ + {{ trans('common.toggle_all') }} + +
+
+
@include('settings.roles.parts.checkbox', ['permission' => 'restrictions-manage-all', 'label' => trans('settings.role_manage_entity_permissions')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'restrictions-manage-own', 'label' => trans('settings.role_manage_own_entity_permissions')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'content-export', 'label' => trans('settings.role_export_content')])
+
+
+
@include('settings.roles.parts.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'users-manage', 'label' => trans('settings.role_manage_users')])
+
@include('settings.roles.parts.checkbox', ['permission' => 'user-roles-manage', 'label' => trans('settings.role_manage_roles')])
+

{{ trans('settings.roles_system_warning') }}

+
+
+
+ +
+ +

{{ trans('settings.role_asset_desc') }}

+ + @if (isset($role) && $role->system_name === 'admin') +

{{ trans('settings.role_asset_admins') }}

+ @endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ trans('common.toggle_all') }} + {{ trans('common.create') }}{{ trans('common.view') }}{{ trans('common.edit') }}{{ trans('common.delete') }}
+
{{ trans('entities.shelves_long') }}
+ {{ trans('common.toggle_all') }} +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-create-all', 'label' => trans('settings.role_all')]) + + @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-view-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-view-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'bookshelf-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.books') }}
+ {{ trans('common.toggle_all') }} +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-create-all', 'label' => trans('settings.role_all')]) + + @include('settings.roles.parts.checkbox', ['permission' => 'book-view-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-view-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'book-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.chapters') }}
+ {{ trans('common.toggle_all') }} +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-create-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-create-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-view-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-view-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'chapter-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.pages') }}
+ {{ trans('common.toggle_all') }} +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-create-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-create-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-view-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-view-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'page-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.images') }}
+ {{ trans('common.toggle_all') }} +
@include('settings.roles.parts.checkbox', ['permission' => 'image-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} + @include('settings.roles.parts.checkbox', ['permission' => 'image-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'image-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'image-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'image-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.attachments') }}
+ {{ trans('common.toggle_all') }} +
@include('settings.roles.parts.checkbox', ['permission' => 'attachment-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} + @include('settings.roles.parts.checkbox', ['permission' => 'attachment-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'attachment-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'attachment-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'attachment-delete-all', 'label' => trans('settings.role_all')]) +
+
{{ trans('entities.comments') }}
+ {{ trans('common.toggle_all') }} +
@include('settings.roles.parts.checkbox', ['permission' => 'comment-create-all', 'label' => '']){{ trans('settings.role_controlled_by_asset') }} + @include('settings.roles.parts.checkbox', ['permission' => 'comment-update-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'comment-update-all', 'label' => trans('settings.role_all')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'comment-delete-own', 'label' => trans('settings.role_own')]) +
+ @include('settings.roles.parts.checkbox', ['permission' => 'comment-delete-all', 'label' => trans('settings.role_all')]) +
+
+
\ No newline at end of file diff --git a/tests/Permissions/RolesTest.php b/tests/Permissions/RolesTest.php index c880bdd00..9368c7163 100644 --- a/tests/Permissions/RolesTest.php +++ b/tests/Permissions/RolesTest.php @@ -163,6 +163,23 @@ class RolesTest extends TestCase $this->assertEquals($this->user->id, $roleA->users()->first()->id); } + public function test_copy_role_button_shown() + { + /** @var Role $role */ + $role = Role::query()->first(); + $resp = $this->asAdmin()->get("/settings/roles/{$role->id}"); + $resp->assertElementContains('a[href$="/roles/new?copy_from=' . $role->id . '"]', 'Copy'); + } + + public function test_copy_from_param_on_create_prefills_with_other_role_data() + { + /** @var Role $role */ + $role = Role::query()->first(); + $resp = $this->asAdmin()->get("/settings/roles/new?copy_from={$role->id}"); + $resp->assertOk(); + $resp->assertElementExists('input[name="display_name"][value="' . ($role->display_name . ' (Copy)') . '"]'); + } + public function test_manage_user_permission() { $this->actingAs($this->user)->get('/settings/users')->assertRedirect('/');