1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-04-21 05:26:10 +03:00

Added hidden public role to fit with new permissions system

This commit is contained in:
Dan Brown 2016-05-01 19:36:53 +01:00
parent 59367b3417
commit 05666efda9
14 changed files with 166 additions and 26 deletions

View File

@ -63,11 +63,13 @@ class PermissionController extends Controller
* Show the form for editing a user role. * Show the form for editing a user role.
* @param $id * @param $id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws PermissionsException
*/ */
public function editRole($id) public function editRole($id)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id); $role = $this->permissionsRepo->getRoleById($id);
if ($role->hidden) throw new PermissionsException('This role cannot be edited');
return view('settings/roles/edit', ['role' => $role]); return view('settings/roles/edit', ['role' => $role]);
} }

View File

@ -49,7 +49,8 @@ class UserController extends Controller
{ {
$this->checkPermission('users-manage'); $this->checkPermission('users-manage');
$authMethod = config('auth.method'); $authMethod = config('auth.method');
return view('users/create', ['authMethod' => $authMethod]); $roles = $this->userRepo->getAssignableRoles();
return view('users/create', ['authMethod' => $authMethod, 'roles' => $roles]);
} }
/** /**
@ -117,7 +118,8 @@ class UserController extends Controller
$user = $this->user->findOrFail($id); $user = $this->user->findOrFail($id);
$activeSocialDrivers = $socialAuthService->getActiveDrivers(); $activeSocialDrivers = $socialAuthService->getActiveDrivers();
$this->setPageTitle('User Profile'); $this->setPageTitle('User Profile');
return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod]); $roles = $this->userRepo->getAssignableRoles();
return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
} }
/** /**

View File

@ -14,6 +14,8 @@ class PermissionsRepo
protected $role; protected $role;
protected $restrictionService; protected $restrictionService;
protected $systemRoles = ['admin', 'public'];
/** /**
* PermissionsRepo constructor. * PermissionsRepo constructor.
* @param Permission $permission * @param Permission $permission
@ -33,7 +35,7 @@ class PermissionsRepo
*/ */
public function getAllRoles() public function getAllRoles()
{ {
return $this->role->all(); return $this->role->where('hidden', '=', false)->get();
} }
/** /**
@ -43,7 +45,7 @@ class PermissionsRepo
*/ */
public function getAllRolesExcept(Role $role) public function getAllRolesExcept(Role $role)
{ {
return $this->role->where('id', '!=', $role->id)->get(); return $this->role->where('id', '!=', $role->id)->where('hidden', '=', false)->get();
} }
/** /**
@ -82,10 +84,14 @@ class PermissionsRepo
* Ensure Admin role always has all permissions. * Ensure Admin role always has all permissions.
* @param $roleId * @param $roleId
* @param $roleData * @param $roleData
* @throws PermissionsException
*/ */
public function updateRole($roleId, $roleData) public function updateRole($roleId, $roleData)
{ {
$role = $this->role->findOrFail($roleId); $role = $this->role->findOrFail($roleId);
if ($role->hidden) throw new PermissionsException("Cannot update a hidden role");
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : []; $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
$this->assignRolePermissions($role, $permissions); $this->assignRolePermissions($role, $permissions);
@ -128,8 +134,8 @@ class PermissionsRepo
$role = $this->role->findOrFail($roleId); $role = $this->role->findOrFail($roleId);
// Prevent deleting admin role or default registration role. // Prevent deleting admin role or default registration role.
if ($role->name === 'admin') { if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
throw new PermissionsException('The admin role cannot be deleted'); throw new PermissionsException('This role is a system role and cannot be deleted');
} else if ($role->id == setting('registration-role')) { } else if ($role->id == setting('registration-role')) {
throw new PermissionsException('This role cannot be deleted while set as the default registration role.'); throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
} }

View File

@ -168,6 +168,15 @@ class UserRepo
]; ];
} }
/**
* Get the roles in the system that are assignable to a user.
* @return mixed
*/
public function getAssignableRoles()
{
return $this->role->visible();
}
/** /**
* Get all the roles which can be given restricted access to * Get all the roles which can be given restricted access to
* other entities in the system. * other entities in the system.
@ -175,7 +184,7 @@ class UserRepo
*/ */
public function getRestrictableRoles() public function getRestrictableRoles()
{ {
return $this->role->where('name', '!=', 'admin')->get(); return $this->role->where('hidden', '=', false)->where('system_name', '=', '')->get();
} }
} }

View File

@ -72,4 +72,24 @@ class Role extends Model
{ {
return static::where('name', '=', $roleName)->first(); return static::where('name', '=', $roleName)->first();
} }
/**
* Get the role object for the specified system role.
* @param $roleName
* @return mixed
*/
public static function getSystemRole($roleName)
{
return static::where('system_name', '=', $roleName)->first();
}
/**
* GEt all visible roles
* @return mixed
*/
public static function visible()
{
return static::where('hidden', '=', false)->orderBy('name')->get();
}
} }

View File

@ -35,9 +35,10 @@ class RestrictionService
public function __construct(EntityPermission $entityPermission, Book $book, Chapter $chapter, Page $page, Role $role) public function __construct(EntityPermission $entityPermission, Book $book, Chapter $chapter, Page $page, Role $role)
{ {
$this->currentUser = auth()->user(); $this->currentUser = auth()->user();
if ($this->currentUser === null) $this->currentUser = new User(['id' => 0]); $userSet = $this->currentUser !== null;
$this->userRoles = $this->currentUser ? $this->currentUser->roles->pluck('id') : []; $this->userRoles = false;
$this->isAdmin = $this->currentUser ? $this->currentUser->hasRole('admin') : false; $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false;
if (!$userSet) $this->currentUser = new User();
$this->entityPermission = $entityPermission; $this->entityPermission = $entityPermission;
$this->role = $role; $this->role = $role;
@ -46,6 +47,28 @@ class RestrictionService
$this->page = $page; $this->page = $page;
} }
/**
* Get the roles for the current user;
* @return array|bool
*/
protected function getRoles()
{
if ($this->userRoles !== false) return $this->userRoles;
$roles = [];
if (auth()->guest()) {
$roles[] = $this->role->getSystemRole('public')->id;
return $roles;
}
foreach ($this->currentUser->roles as $role) {
$roles[] = $role->id;
}
return $roles;
}
/** /**
* Re-generate all entity permission from scratch. * Re-generate all entity permission from scratch.
*/ */
@ -346,7 +369,7 @@ class RestrictionService
{ {
return $query->where(function ($parentQuery) { return $query->where(function ($parentQuery) {
$parentQuery->whereHas('permissions', function ($permissionQuery) { $parentQuery->whereHas('permissions', function ($permissionQuery) {
$permissionQuery->whereIn('role_id', $this->userRoles) $permissionQuery->whereIn('role_id', $this->getRoles())
->where('action', '=', $this->currentAction) ->where('action', '=', $this->currentAction)
->where(function ($query) { ->where(function ($query) {
$query->where('has_permission', '=', true) $query->where('has_permission', '=', true)
@ -428,7 +451,7 @@ class RestrictionService
->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) ->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
->whereRaw('entity_permissions.entity_type=' . $tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn']) ->whereRaw('entity_permissions.entity_type=' . $tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'])
->where('action', '=', $this->currentAction) ->where('action', '=', $this->currentAction)
->whereIn('role_id', $this->userRoles) ->whereIn('role_id', $this->getRoles())
->where(function ($query) { ->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true) $query->where('has_permission_own', '=', true)
@ -460,7 +483,7 @@ class RestrictionService
->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) ->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
->where('entity_type', '=', 'Bookstack\\Page') ->where('entity_type', '=', 'Bookstack\\Page')
->where('action', '=', $this->currentAction) ->where('action', '=', $this->currentAction)
->whereIn('role_id', $this->userRoles) ->whereIn('role_id', $this->getRoles())
->where(function ($query) { ->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true) $query->where('has_permission_own', '=', true)

View File

@ -39,7 +39,6 @@ if (!function_exists('versioned_asset')) {
*/ */
function userCan($permission, \BookStack\Ownable $ownable = null) function userCan($permission, \BookStack\Ownable $ownable = null)
{ {
if (!auth()->check()) return false;
if ($ownable === null) { if ($ownable === null) {
return auth()->user() && auth()->user()->can($permission); return auth()->user() && auth()->user()->can($permission);
} }

View File

@ -21,12 +21,53 @@ class CreateEntityPermissionsTable extends Migration
$table->boolean('has_permission')->default(false); $table->boolean('has_permission')->default(false);
$table->boolean('has_permission_own')->default(false); $table->boolean('has_permission_own')->default(false);
$table->integer('created_by'); $table->integer('created_by');
// Create indexes
$table->index(['entity_id', 'entity_type']); $table->index(['entity_id', 'entity_type']);
$table->index('has_permission');
$table->index('has_permission_own');
$table->index('role_id'); $table->index('role_id');
$table->index('action'); $table->index('action');
$table->index('created_by'); $table->index('created_by');
}); });
Schema::table('roles', function (Blueprint $table) {
$table->string('system_name');
$table->boolean('hidden')->default(false);
$table->index('hidden');
$table->index('system_name');
});
// Create the new public role
$publicRole = new \BookStack\Role();
$publicRole->name = 'public';
$publicRole->display_name = 'Public';
$publicRole->description = 'The role given to public visitors if allowed';
$publicRole->system_name = 'public';
$publicRole->hidden = true;
// Ensure unique name
while (\BookStack\Role::getRole($publicRole->name) !== null) {
$publicRole->name = $publicRole->name . str_random(2);
}
$publicRole->save();
// Add new view permissions to public role
$entities = ['Book', 'Page', 'Chapter'];
$ops = ['View All', 'View Own'];
foreach ($entities as $entity) {
foreach ($ops as $op) {
$name = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
$permission = \BookStack\Permission::getByName($name);
// Assign view permissions to public
$publicRole->attachPermission($permission);
}
}
// Update admin role with system name
$admin = \BookStack\Role::getRole('admin');
$admin->system_name = 'admin';
$admin->save();
// Generate the new entity permissions
$restrictionService = app(\BookStack\Services\RestrictionService::class); $restrictionService = app(\BookStack\Services\RestrictionService::class);
$restrictionService->buildEntityPermissions(); $restrictionService->buildEntityPermissions();
} }
@ -39,5 +80,13 @@ class CreateEntityPermissionsTable extends Migration
public function down() public function down()
{ {
Schema::drop('entity_permissions'); Schema::drop('entity_permissions');
// Delete the public role
$public = \BookStack\Role::getSystemRole('public');
$public->delete();
Schema::table('roles', function (Blueprint $table) {
$table->dropColumn('system_name');
});
} }
} }

View File

@ -49,9 +49,15 @@
<hr> <hr>
<p class="text-muted">No pages are currently in this chapter.</p> <p class="text-muted">No pages are currently in this chapter.</p>
<p> <p>
<a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a> @if(userCan('page-create', $chapter))
&nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp; <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
<a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a> @endif
@if(userCan('page-create', $chapter) && userCan('book-update', $book))
&nbsp;&nbsp;<em class="text-muted">-or-</em>&nbsp;&nbsp;&nbsp;
@endif
@if(userCan('book-update', $book))
<a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
@endif
</p> </p>
<hr> <hr>
@endif @endif

View File

@ -66,8 +66,8 @@
<div class="form-group"> <div class="form-group">
<label for="setting-registration-role">Default user role after registration</label> <label for="setting-registration-role">Default user role after registration</label>
<select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif> <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
@foreach(\BookStack\Role::all() as $role) @foreach(\BookStack\Role::visible() as $role)
<option value="{{$role->id}}" <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
@if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif @if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif
> >
{{ $role->display_name }} {{ $role->display_name }}

View File

@ -31,11 +31,11 @@
</p> </p>
<table class="table"> <table class="table">
<tr> <tr>
<th></th> <th width="20%"></th>
<th>Create</th> <th width="20%">Create</th>
<th>View</th> <th width="20%">View</th>
<th>Edit</th> <th width="20%">Edit</th>
<th>Delete</th> <th width="20%">Delete</th>
</tr> </tr>
<tr> <tr>
<td>Books</td> <td>Books</td>
@ -96,6 +96,7 @@
<tr> <tr>
<td>Images</td> <td>Images</td>
<td>@include('settings/roles/checkbox', ['permission' => 'image-create-all'])</td> <td>@include('settings/roles/checkbox', ['permission' => 'image-create-all'])</td>
<td style="line-height:1.2;"><small class="faded">Controlled by the asset they are uploaded to</small></td>
<td> <td>
<label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) Own</label> <label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) All</label> <label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) All</label>

View File

@ -13,7 +13,7 @@
@if(userCan('users-manage')) @if(userCan('users-manage'))
<div class="form-group"> <div class="form-group">
<label for="role">User Role</label> <label for="role">User Role</label>
@include('form/role-checkboxes', ['name' => 'roles', 'roles' => \BookStack\Role::all()]) @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
</div> </div>
@endif @endif

View File

@ -11,7 +11,7 @@
@if(userCan('users-manage')) @if(userCan('users-manage'))
<div class="form-group"> <div class="form-group">
<label for="role">User Role</label> <label for="role">User Role</label>
@include('form/role-checkboxes', ['name' => 'roles', 'roles' => \BookStack\Role::all()]) @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
</div> </div>
@endif @endif

View File

@ -544,4 +544,27 @@ class RolesTest extends TestCase
->dontSeeInElement('.book-content', $otherPage->name); ->dontSeeInElement('.book-content', $otherPage->name);
} }
public function test_public_role_not_visible_in_user_edit_screen()
{
$user = \BookStack\User::first();
$this->asAdmin()->visit('/settings/users/' . $user->id)
->seeElement('#roles-admin')
->dontSeeElement('#roles-public');
}
public function test_public_role_not_visible_in_role_listing()
{
$this->asAdmin()->visit('/settings/roles')
->see('Admin')
->dontSee('Public');
}
public function test_public_role_not_visible_in_default_role_setting()
{
$this->asAdmin()->visit('/settings')
->seeElement('[data-role-name="admin"]')
->dontSeeElement('[data-role-name="public"]');
}
} }