Removed role 'name' field from database

The 'name' field was really redundant and caused confusion in the
codebase, since the 'Display' name is often used and we have a
'system_name' for the admin and public role.

This fixes #2032, Where external auth group matching has confusing
behaviour as matching was done against the display_name, if no
external_auth field is set, but only roles with a match 'name' field
would be considered.

This also fixes and error where the role users migration, on role
delete, would not actually fire due to mis-matching http body keys.
Looks like this has been an issue from the start. Added some testing to
cover. Fixes #2211.

Also converted phpdoc to typehints in many areas of the reviewed code
during the above.
This commit is contained in:
Dan Brown 2020-08-04 14:55:01 +01:00
parent a9f02550f0
commit 5f1ee5fb0e
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
15 changed files with 142 additions and 122 deletions

View File

@ -3,6 +3,8 @@
use BookStack\Auth\Role; use BookStack\Auth\Role;
use BookStack\Auth\User; use BookStack\Auth\User;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class ExternalAuthService class ExternalAuthService
{ {
@ -39,22 +41,14 @@ class ExternalAuthService
/** /**
* Match an array of group names to BookStack system roles. * Match an array of group names to BookStack system roles.
* Formats group names to be lower-case and hyphenated. * Formats group names to be lower-case and hyphenated.
* @param array $groupNames
* @return \Illuminate\Support\Collection
*/ */
protected function matchGroupsToSystemsRoles(array $groupNames) protected function matchGroupsToSystemsRoles(array $groupNames): Collection
{ {
foreach ($groupNames as $i => $groupName) { foreach ($groupNames as $i => $groupName) {
$groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName))); $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
} }
$roles = Role::query()->where(function (Builder $query) use ($groupNames) { $roles = Role::query()->get(['id', 'external_auth_id', 'display_name']);
$query->whereIn('name', $groupNames);
foreach ($groupNames as $groupName) {
$query->orWhere('external_auth_id', 'LIKE', '%' . $groupName . '%');
}
})->get();
$matchedRoles = $roles->filter(function (Role $role) use ($groupNames) { $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
return $this->roleMatchesGroupNames($role, $groupNames); return $this->roleMatchesGroupNames($role, $groupNames);
}); });

View File

@ -1,8 +1,9 @@
<?php namespace BookStack\Auth\Permissions; <?php namespace BookStack\Auth\Permissions;
use BookStack\Auth\Permissions;
use BookStack\Auth\Role; use BookStack\Auth\Role;
use BookStack\Exceptions\PermissionsException; use BookStack\Exceptions\PermissionsException;
use Exception;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class PermissionsRepo class PermissionsRepo
@ -16,11 +17,8 @@ class PermissionsRepo
/** /**
* PermissionsRepo constructor. * PermissionsRepo constructor.
* @param RolePermission $permission
* @param Role $role
* @param \BookStack\Auth\Permissions\PermissionService $permissionService
*/ */
public function __construct(RolePermission $permission, Role $role, Permissions\PermissionService $permissionService) public function __construct(RolePermission $permission, Role $role, PermissionService $permissionService)
{ {
$this->permission = $permission; $this->permission = $permission;
$this->role = $role; $this->role = $role;
@ -29,46 +27,34 @@ class PermissionsRepo
/** /**
* Get all the user roles from the system. * Get all the user roles from the system.
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/ */
public function getAllRoles() public function getAllRoles(): Collection
{ {
return $this->role->all(); return $this->role->all();
} }
/** /**
* Get all the roles except for the provided one. * Get all the roles except for the provided one.
* @param Role $role
* @return mixed
*/ */
public function getAllRolesExcept(Role $role) public function getAllRolesExcept(Role $role): Collection
{ {
return $this->role->where('id', '!=', $role->id)->get(); return $this->role->where('id', '!=', $role->id)->get();
} }
/** /**
* Get a role via its ID. * Get a role via its ID.
* @param $id
* @return mixed
*/ */
public function getRoleById($id) public function getRoleById($id): Role
{ {
return $this->role->findOrFail($id); return $this->role->newQuery()->findOrFail($id);
} }
/** /**
* Save a new role into the system. * Save a new role into the system.
* @param array $roleData
* @return Role
*/ */
public function saveNewRole($roleData) public function saveNewRole(array $roleData): Role
{ {
$role = $this->role->newInstance($roleData); $role = $this->role->newInstance($roleData);
$role->name = str_replace(' ', '-', strtolower($roleData['display_name']));
// Prevent duplicate names
while ($this->role->where('name', '=', $role->name)->count() > 0) {
$role->name .= strtolower(Str::random(2));
}
$role->save(); $role->save();
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : []; $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
@ -80,13 +66,11 @@ class PermissionsRepo
/** /**
* Updates an existing role. * Updates an existing role.
* Ensure Admin role always have core permissions. * Ensure Admin role always have core permissions.
* @param $roleId
* @param $roleData
* @throws PermissionsException
*/ */
public function updateRole($roleId, $roleData) public function updateRole($roleId, array $roleData)
{ {
$role = $this->role->findOrFail($roleId); /** @var Role $role */
$role = $this->role->newQuery()->findOrFail($roleId);
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : []; $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
if ($role->system_name === 'admin') { if ($role->system_name === 'admin') {
@ -108,16 +92,19 @@ class PermissionsRepo
/** /**
* Assign an list of permission names to an role. * Assign an list of permission names to an role.
* @param Role $role
* @param array $permissionNameArray
*/ */
public function assignRolePermissions(Role $role, $permissionNameArray = []) public function assignRolePermissions(Role $role, array $permissionNameArray = [])
{ {
$permissions = []; $permissions = [];
$permissionNameArray = array_values($permissionNameArray); $permissionNameArray = array_values($permissionNameArray);
if ($permissionNameArray && count($permissionNameArray) > 0) {
$permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray(); if ($permissionNameArray) {
$permissions = $this->permission->newQuery()
->whereIn('name', $permissionNameArray)
->pluck('id')
->toArray();
} }
$role->permissions()->sync($permissions); $role->permissions()->sync($permissions);
} }
@ -126,13 +113,13 @@ class PermissionsRepo
* Check it's not an admin role or set as default before deleting. * Check it's not an admin role or set as default before deleting.
* If an migration Role ID is specified the users assign to the current role * If an migration Role ID is specified the users assign to the current role
* will be added to the role of the specified id. * will be added to the role of the specified id.
* @param $roleId
* @param $migrateRoleId
* @throws PermissionsException * @throws PermissionsException
* @throws Exception
*/ */
public function deleteRole($roleId, $migrateRoleId) public function deleteRole($roleId, $migrateRoleId)
{ {
$role = $this->role->findOrFail($roleId); /** @var Role $role */
$role = $this->role->newQuery()->findOrFail($roleId);
// Prevent deleting admin role or default registration role. // Prevent deleting admin role or default registration role.
if ($role->system_name && in_array($role->system_name, $this->systemRoles)) { if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
@ -142,9 +129,9 @@ class PermissionsRepo
} }
if ($migrateRoleId) { if ($migrateRoleId) {
$newRole = $this->role->find($migrateRoleId); $newRole = $this->role->newQuery()->find($migrateRoleId);
if ($newRole) { if ($newRole) {
$users = $role->users->pluck('id')->toArray(); $users = $role->users()->pluck('id')->toArray();
$newRole->users()->sync($users); $newRole->users()->sync($users);
} }
} }

View File

@ -3,6 +3,9 @@
use BookStack\Auth\Role; use BookStack\Auth\Role;
use BookStack\Model; use BookStack\Model;
/**
* @property int $id
*/
class RolePermission extends Model class RolePermission extends Model
{ {
/** /**

View File

@ -3,13 +3,16 @@
use BookStack\Auth\Permissions\JointPermission; use BookStack\Auth\Permissions\JointPermission;
use BookStack\Auth\Permissions\RolePermission; use BookStack\Auth\Permissions\RolePermission;
use BookStack\Model; use BookStack\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
/** /**
* Class Role * Class Role
* @property int $id
* @property string $display_name * @property string $display_name
* @property string $description * @property string $description
* @property string $external_auth_id * @property string $external_auth_id
* @package BookStack\Auth * @property string $system_name
*/ */
class Role extends Model class Role extends Model
{ {
@ -26,9 +29,8 @@ class Role extends Model
/** /**
* Get all related JointPermissions. * Get all related JointPermissions.
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
public function jointPermissions() public function jointPermissions(): HasMany
{ {
return $this->hasMany(JointPermission::class); return $this->hasMany(JointPermission::class);
} }
@ -43,10 +45,8 @@ class Role extends Model
/** /**
* Check if this role has a permission. * Check if this role has a permission.
* @param $permissionName
* @return bool
*/ */
public function hasPermission($permissionName) public function hasPermission(string $permissionName): bool
{ {
$permissions = $this->getRelationValue('permissions'); $permissions = $this->getRelationValue('permissions');
foreach ($permissions as $permission) { foreach ($permissions as $permission) {
@ -59,7 +59,6 @@ class Role extends Model
/** /**
* Add a permission to this role. * Add a permission to this role.
* @param RolePermission $permission
*/ */
public function attachPermission(RolePermission $permission) public function attachPermission(RolePermission $permission)
{ {
@ -68,7 +67,6 @@ class Role extends Model
/** /**
* Detach a single permission from this role. * Detach a single permission from this role.
* @param RolePermission $permission
*/ */
public function detachPermission(RolePermission $permission) public function detachPermission(RolePermission $permission)
{ {
@ -76,39 +74,33 @@ class Role extends Model
} }
/** /**
* Get the role object for the specified role. * Get the role of the specified display name.
* @param $roleName
* @return Role
*/ */
public static function getRole($roleName) public static function getRole(string $displayName): ?Role
{ {
return static::query()->where('name', '=', $roleName)->first(); return static::query()->where('display_name', '=', $displayName)->first();
} }
/** /**
* Get the role object for the specified system role. * Get the role object for the specified system role.
* @param $roleName
* @return Role
*/ */
public static function getSystemRole($roleName) public static function getSystemRole(string $systemName): ?Role
{ {
return static::query()->where('system_name', '=', $roleName)->first(); return static::query()->where('system_name', '=', $systemName)->first();
} }
/** /**
* Get all visible roles * Get all visible roles
* @return mixed
*/ */
public static function visible() public static function visible(): Collection
{ {
return static::query()->where('hidden', '=', false)->orderBy('name')->get(); return static::query()->where('hidden', '=', false)->orderBy('name')->get();
} }
/** /**
* Get the roles that can be restricted. * Get the roles that can be restricted.
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
*/ */
public static function restrictable() public static function restrictable(): Collection
{ {
return static::query()->where('system_name', '!=', 'admin')->get(); return static::query()->where('system_name', '!=', 'admin')->get();
} }

View File

@ -101,12 +101,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
/** /**
* Check if the user has a role. * Check if the user has a role.
* @param $role
* @return mixed
*/ */
public function hasRole($role) public function hasRole($roleId): bool
{ {
return $this->roles->pluck('name')->contains($role); return $this->roles->pluck('id')->contains($roleId);
} }
/** /**
@ -163,7 +161,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
/** /**
* Attach a role to this user. * Attach a role to this user.
* @param Role $role
*/ */
public function attachRole(Role $role) public function attachRole(Role $role)
{ {

View File

@ -238,7 +238,7 @@ class UserRepo
*/ */
public function getAllRoles() public function getAllRoles()
{ {
return $this->role->newQuery()->orderBy('name', 'asc')->get(); return $this->role->newQuery()->orderBy('display_name', 'asc')->get();
} }
/** /**

View File

@ -2,7 +2,9 @@
use BookStack\Auth\Permissions\PermissionsRepo; use BookStack\Auth\Permissions\PermissionsRepo;
use BookStack\Exceptions\PermissionsException; use BookStack\Exceptions\PermissionsException;
use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class PermissionController extends Controller class PermissionController extends Controller
{ {
@ -11,7 +13,6 @@ class PermissionController extends Controller
/** /**
* PermissionController constructor. * PermissionController constructor.
* @param \BookStack\Auth\Permissions\PermissionsRepo $permissionsRepo
*/ */
public function __construct(PermissionsRepo $permissionsRepo) public function __construct(PermissionsRepo $permissionsRepo)
{ {
@ -31,7 +32,6 @@ class PermissionController extends Controller
/** /**
* Show the form to create a new role * Show the form to create a new role
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/ */
public function createRole() public function createRole()
{ {
@ -41,15 +41,13 @@ class PermissionController extends Controller
/** /**
* Store a new role in the system. * Store a new role in the system.
* @param Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function storeRole(Request $request) public function storeRole(Request $request)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');
$this->validate($request, [ $this->validate($request, [
'display_name' => 'required|min:3|max:200', 'display_name' => 'required|min:3|max:180',
'description' => 'max:250' 'description' => 'max:180'
]); ]);
$this->permissionsRepo->saveNewRole($request->all()); $this->permissionsRepo->saveNewRole($request->all());
@ -59,11 +57,9 @@ class PermissionController extends Controller
/** /**
* Show the form for editing a user role. * Show the form for editing a user role.
* @param $id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws PermissionsException * @throws PermissionsException
*/ */
public function editRole($id) public function editRole(string $id)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id); $role = $this->permissionsRepo->getRoleById($id);
@ -75,18 +71,14 @@ class PermissionController extends Controller
/** /**
* Updates a user role. * Updates a user role.
* @param Request $request * @throws ValidationException
* @param $id
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws PermissionsException
* @throws \Illuminate\Validation\ValidationException
*/ */
public function updateRole(Request $request, $id) public function updateRole(Request $request, string $id)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');
$this->validate($request, [ $this->validate($request, [
'display_name' => 'required|min:3|max:200', 'display_name' => 'required|min:3|max:180',
'description' => 'max:250' 'description' => 'max:180'
]); ]);
$this->permissionsRepo->updateRole($id, $request->all()); $this->permissionsRepo->updateRole($id, $request->all());
@ -97,10 +89,8 @@ class PermissionController extends Controller
/** /**
* Show the view to delete a role. * Show the view to delete a role.
* Offers the chance to migrate users. * Offers the chance to migrate users.
* @param $id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/ */
public function showDeleteRole($id) public function showDeleteRole(string $id)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id); $role = $this->permissionsRepo->getRoleById($id);
@ -113,11 +103,9 @@ class PermissionController extends Controller
/** /**
* Delete a role from the system, * Delete a role from the system,
* Migrate from a previous role if set. * Migrate from a previous role if set.
* @param Request $request * @throws Exception
* @param $id
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function deleteRole(Request $request, $id) public function deleteRole(Request $request, string $id)
{ {
$this->checkPermission('user-roles-manage'); $this->checkPermission('user-roles-manage');

View File

@ -66,8 +66,8 @@ class UserController extends Controller
{ {
$this->checkPermission('users-manage'); $this->checkPermission('users-manage');
$validationRules = [ $validationRules = [
'name' => 'required', 'name' => 'required',
'email' => 'required|email|unique:users,email' 'email' => 'required|email|unique:users,email'
]; ];
$authMethod = config('auth.method'); $authMethod = config('auth.method');

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class RemoveRoleNameField extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('roles', function (Blueprint $table) {
$table->dropColumn('name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('roles', function (Blueprint $table) {
$table->string('name')->index();
});
DB::table('roles')->update([
"name" => DB::raw("lower(replace(`display_name`, ' ', '-'))"),
]);
}
}

View File

@ -3,10 +3,10 @@
@foreach($roles as $role) @foreach($roles as $role)
<div> <div>
@include('components.custom-checkbox', [ @include('components.custom-checkbox', [
'name' => $name . '[' . str_replace('.', 'DOT', $role->name) . ']', 'name' => $name . '[' . strval($role->id) . ']',
'label' => $role->display_name, 'label' => $role->display_name,
'value' => $role->id, 'value' => $role->id,
'checked' => old($name . '.' . str_replace('.', 'DOT', $role->name)) || (!old('name') && isset($model) && $model->hasRole($role->name)) 'checked' => old($name . '.' . strval($role->id)) || (!old('name') && isset($model) && $model->hasRole($role->id))
]) ])
</div> </div>
@endforeach @endforeach

View File

@ -231,7 +231,8 @@
<label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</label> <label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</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\Auth\Role::all() as $role) @foreach(\BookStack\Auth\Role::all() as $role)
<option value="{{$role->id}}" data-role-name="{{ $role->name }}" <option value="{{$role->id}}"
data-system-role-name="{{ $role->system_name ?? '' }}"
@if(setting('registration-role', \BookStack\Auth\Role::first()->id) == $role->id) selected @endif @if(setting('registration-role', \BookStack\Auth\Role::first()->id) == $role->id) selected @endif
> >
{{ $role->display_name }} {{ $role->display_name }}

View File

@ -19,7 +19,7 @@
@if($role->users->count() > 0) @if($role->users->count() > 0)
<div class="form-group"> <div class="form-group">
<p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p> <p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p>
@include('form.role-select', ['options' => $roles, 'name' => 'migration_role_id']) @include('form.role-select', ['options' => $roles, 'name' => 'migrate_role_id'])
</div> </div>
@endif @endif

View File

@ -213,13 +213,14 @@ class AuthTest extends BrowserKitTest
public function test_user_creation() public function test_user_creation()
{ {
$user = factory(User::class)->make(); $user = factory(User::class)->make();
$adminRole = Role::getRole('admin');
$this->asAdmin() $this->asAdmin()
->visit('/settings/users') ->visit('/settings/users')
->click('Add New User') ->click('Add New User')
->type($user->name, '#name') ->type($user->name, '#name')
->type($user->email, '#email') ->type($user->email, '#email')
->check('roles[admin]') ->check("roles[{$adminRole->id}]")
->type($user->password, '#password') ->type($user->password, '#password')
->type($user->password, '#password-confirm') ->type($user->password, '#password-confirm')
->press('Save') ->press('Save')

View File

@ -237,9 +237,9 @@ class LdapTest extends BrowserKitTest
public function test_login_maps_roles_and_retains_existing_roles() public function test_login_maps_roles_and_retains_existing_roles()
{ {
$roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']); $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
$roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']); $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
$existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']); $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
$this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save(); $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
$this->mockUser->attachRole($existingRole); $this->mockUser->attachRole($existingRole);
@ -283,8 +283,8 @@ class LdapTest extends BrowserKitTest
public function test_login_maps_roles_and_removes_old_roles_if_set() public function test_login_maps_roles_and_removes_old_roles_if_set()
{ {
$roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']); $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
$existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']); $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
$this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save(); $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
$this->mockUser->attachRole($existingRole); $this->mockUser->attachRole($existingRole);
@ -323,15 +323,15 @@ class LdapTest extends BrowserKitTest
public function test_external_auth_id_visible_in_roles_page_when_ldap_active() public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
{ {
$role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']); $role = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
$this->asAdmin()->visit('/settings/roles/' . $role->id) $this->asAdmin()->visit('/settings/roles/' . $role->id)
->see('ex-auth-a'); ->see('ex-auth-a');
} }
public function test_login_maps_roles_using_external_auth_ids_if_set() public function test_login_maps_roles_using_external_auth_ids_if_set()
{ {
$roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']); $roleToReceive = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
$roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']); $roleToNotReceive = factory(Role::class)->create(['display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
app('config')->set([ app('config')->set([
'services.ldap.user_to_groups' => true, 'services.ldap.user_to_groups' => true,
@ -368,8 +368,8 @@ class LdapTest extends BrowserKitTest
public function test_login_group_mapping_does_not_conflict_with_default_role() public function test_login_group_mapping_does_not_conflict_with_default_role()
{ {
$roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']); $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
$roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']); $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
$this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save(); $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
setting()->put('registration-role', $roleToReceive->id); setting()->put('registration-role', $roleToReceive->id);

View File

@ -57,7 +57,7 @@ class RolesTest extends BrowserKitTest
->type('Test Role', 'display_name') ->type('Test Role', 'display_name')
->type('A little test description', 'description') ->type('A little test description', 'description')
->press('Save Role') ->press('Save Role')
->seeInDatabase('roles', ['display_name' => $testRoleName, 'name' => 'test-role', 'description' => $testRoleDesc]) ->seeInDatabase('roles', ['display_name' => $testRoleName, 'description' => $testRoleDesc])
->seePageIs('/settings/roles'); ->seePageIs('/settings/roles');
// Updating // Updating
$this->asAdmin()->visit('/settings/roles') $this->asAdmin()->visit('/settings/roles')
@ -65,7 +65,7 @@ class RolesTest extends BrowserKitTest
->click($testRoleName) ->click($testRoleName)
->type($testRoleUpdateName, '#display_name') ->type($testRoleUpdateName, '#display_name')
->press('Save Role') ->press('Save Role')
->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'name' => 'test-role', 'description' => $testRoleDesc]) ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'description' => $testRoleDesc])
->seePageIs('/settings/roles'); ->seePageIs('/settings/roles');
// Deleting // Deleting
$this->asAdmin()->visit('/settings/roles') $this->asAdmin()->visit('/settings/roles')
@ -99,6 +99,25 @@ class RolesTest extends BrowserKitTest
$this->see('This user is the only user assigned to the administrator role'); $this->see('This user is the only user assigned to the administrator role');
} }
public function test_migrate_users_on_delete_works()
{
$roleA = Role::query()->create(['display_name' => 'Delete Test A']);
$roleB = Role::query()->create(['display_name' => 'Delete Test B']);
$this->user->attachRole($roleB);
$this->assertCount(0, $roleA->users()->get());
$this->assertCount(1, $roleB->users()->get());
$deletePage = $this->asAdmin()->get("/settings/roles/delete/{$roleB->id}");
$deletePage->seeElement('select[name=migrate_role_id]');
$this->asAdmin()->delete("/settings/roles/delete/{$roleB->id}", [
'migrate_role_id' => $roleA->id,
]);
$this->assertCount(1, $roleA->users()->get());
$this->assertEquals($this->user->id, $roleA->users()->first()->id);
}
public function test_manage_user_permission() public function test_manage_user_permission()
{ {
$this->actingAs($this->user)->visit('/settings/users') $this->actingAs($this->user)->visit('/settings/users')
@ -667,9 +686,11 @@ class RolesTest extends BrowserKitTest
public function test_public_role_visible_in_user_edit_screen() public function test_public_role_visible_in_user_edit_screen()
{ {
$user = \BookStack\Auth\User::first(); $user = \BookStack\Auth\User::first();
$adminRole = Role::getSystemRole('admin');
$publicRole = Role::getSystemRole('public');
$this->asAdmin()->visit('/settings/users/' . $user->id) $this->asAdmin()->visit('/settings/users/' . $user->id)
->seeElement('[name="roles[admin]"]') ->seeElement('[name="roles['.$adminRole->id.']"]')
->seeElement('[name="roles[public]"]'); ->seeElement('[name="roles['.$publicRole->id.']"]');
} }
public function test_public_role_visible_in_role_listing() public function test_public_role_visible_in_role_listing()
@ -682,9 +703,8 @@ class RolesTest extends BrowserKitTest
public function test_public_role_visible_in_default_role_setting() public function test_public_role_visible_in_default_role_setting()
{ {
$this->asAdmin()->visit('/settings') $this->asAdmin()->visit('/settings')
->seeElement('[data-role-name="admin"]') ->seeElement('[data-system-role-name="admin"]')
->seeElement('[data-role-name="public"]'); ->seeElement('[data-system-role-name="public"]');
} }
public function test_public_role_not_deleteable() public function test_public_role_not_deleteable()