* Show the form for editing a user role.
* @param $id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+ * @throws PermissionsException
*/
public function editRole($id)
{
$this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id);
+ if ($role->hidden) throw new PermissionsException('This role cannot be edited');
return view('settings/roles/edit', ['role' => $role]);
}
{
$this->checkPermission('users-manage');
$authMethod = config('auth.method');
- return view('users/create', ['authMethod' => $authMethod]);
+ $roles = $this->userRepo->getAssignableRoles();
+ return view('users/create', ['authMethod' => $authMethod, 'roles' => $roles]);
}
/**
$user = $this->user->findOrFail($id);
$activeSocialDrivers = $socialAuthService->getActiveDrivers();
$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]);
}
/**
protected $role;
protected $restrictionService;
+ protected $systemRoles = ['admin', 'public'];
+
/**
* PermissionsRepo constructor.
* @param Permission $permission
*/
public function getAllRoles()
{
- return $this->role->all();
+ return $this->role->where('hidden', '=', false)->get();
}
/**
*/
public function getAllRolesExcept(Role $role)
{
- return $this->role->where('id', '!=', $role->id)->get();
+ return $this->role->where('id', '!=', $role->id)->where('hidden', '=', false)->get();
}
/**
* Ensure Admin role always has all permissions.
* @param $roleId
* @param $roleData
+ * @throws PermissionsException
*/
public function updateRole($roleId, $roleData)
{
$role = $this->role->findOrFail($roleId);
+
+ if ($role->hidden) throw new PermissionsException("Cannot update a hidden role");
+
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
$this->assignRolePermissions($role, $permissions);
$role = $this->role->findOrFail($roleId);
// Prevent deleting admin role or default registration role.
- if ($role->name === 'admin') {
- throw new PermissionsException('The admin role cannot be deleted');
+ if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
+ throw new PermissionsException('This role is a system role and cannot be deleted');
} else if ($role->id == setting('registration-role')) {
throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
}
];
}
+ /**
+ * 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
* other entities in the system.
*/
public function getRestrictableRoles()
{
- return $this->role->where('name', '!=', 'admin')->get();
+ return $this->role->where('hidden', '=', false)->where('system_name', '=', '')->get();
}
}
\ No newline at end of file
{
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();
+ }
+
}
public function __construct(EntityPermission $entityPermission, Book $book, Chapter $chapter, Page $page, Role $role)
{
$this->currentUser = auth()->user();
- if ($this->currentUser === null) $this->currentUser = new User(['id' => 0]);
- $this->userRoles = $this->currentUser ? $this->currentUser->roles->pluck('id') : [];
- $this->isAdmin = $this->currentUser ? $this->currentUser->hasRole('admin') : false;
+ $userSet = $this->currentUser !== null;
+ $this->userRoles = false;
+ $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false;
+ if (!$userSet) $this->currentUser = new User();
$this->entityPermission = $entityPermission;
$this->role = $role;
$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.
*/
{
return $query->where(function ($parentQuery) {
$parentQuery->whereHas('permissions', function ($permissionQuery) {
- $permissionQuery->whereIn('role_id', $this->userRoles)
+ $permissionQuery->whereIn('role_id', $this->getRoles())
->where('action', '=', $this->currentAction)
->where(function ($query) {
$query->where('has_permission', '=', true)
->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
->whereRaw('entity_permissions.entity_type=' . $tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'])
->where('action', '=', $this->currentAction)
- ->whereIn('role_id', $this->userRoles)
+ ->whereIn('role_id', $this->getRoles())
->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true)
->whereRaw('entity_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
->where('entity_type', '=', 'Bookstack\\Page')
->where('action', '=', $this->currentAction)
- ->whereIn('role_id', $this->userRoles)
+ ->whereIn('role_id', $this->getRoles())
->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true)
*/
function userCan($permission, \BookStack\Ownable $ownable = null)
{
- if (!auth()->check()) return false;
if ($ownable === null) {
return auth()->user() && auth()->user()->can($permission);
}
$table->boolean('has_permission')->default(false);
$table->boolean('has_permission_own')->default(false);
$table->integer('created_by');
+ // Create indexes
$table->index(['entity_id', 'entity_type']);
+ $table->index('has_permission');
+ $table->index('has_permission_own');
$table->index('role_id');
$table->index('action');
$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->buildEntityPermissions();
}
public function down()
{
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');
+ });
}
}
<hr>
<p class="text-muted">No pages are currently in this chapter.</p>
<p>
- <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
- <em class="text-muted">-or-</em>
- <a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
+ @if(userCan('page-create', $chapter))
+ <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
+ @endif
+ @if(userCan('page-create', $chapter) && userCan('book-update', $book))
+ <em class="text-muted">-or-</em>
+ @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>
<hr>
@endif
<div class="form-group">
<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>
- @foreach(\BookStack\Role::all() as $role)
- <option value="{{$role->id}}"
+ @foreach(\BookStack\Role::visible() as $role)
+ <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
@if(setting('registration-role', \BookStack\Role::first()->id) == $role->id) selected @endif
>
{{ $role->display_name }}
</p>
<table class="table">
<tr>
- <th></th>
- <th>Create</th>
- <th>View</th>
- <th>Edit</th>
- <th>Delete</th>
+ <th width="20%"></th>
+ <th width="20%">Create</th>
+ <th width="20%">View</th>
+ <th width="20%">Edit</th>
+ <th width="20%">Delete</th>
</tr>
<tr>
<td>Books</td>
<tr>
<td>Images</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>
<label>@include('settings/roles/checkbox', ['permission' => 'image-update-own']) Own</label>
<label>@include('settings/roles/checkbox', ['permission' => 'image-update-all']) All</label>
@if(userCan('users-manage'))
<div class="form-group">
<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>
@endif
@if(userCan('users-manage'))
<div class="form-group">
<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>
@endif
->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"]');
+
+ }
+
}