162 lines
8.9 KiB
PHP
162 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\SystemSettings;
|
|
|
|
use App\Models\Auth\DingTalkIdentity;
|
|
use App\Models\Auth\Permission;
|
|
use App\Models\Auth\Role;
|
|
use App\Models\Auth\User;
|
|
use App\Utilities\BusinessNavigation;
|
|
use App\Utilities\SidebarPermissions;
|
|
use App\Utilities\SystemAccess;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class UserPermissionsTest extends FeatureTestCase
|
|
{
|
|
public function test_admin_sees_all_company_accounts_but_cannot_edit_administrators(): void
|
|
{
|
|
$admin = $this->account('admin');
|
|
$regular = $this->account('user');
|
|
$outside = $this->account('user');
|
|
$outside->companies()->detach();
|
|
$this->loginAs($admin)->get(route('system-settings.users.index'))
|
|
->assertOk()->assertSeeText($this->user->name)->assertSeeText($admin->name)->assertSeeText($regular->name)
|
|
->assertDontSeeText($outside->name)
|
|
->assertDontSee(route('system-settings.users.edit', $this->user->id), false)
|
|
->assertSee(route('system-settings.users.edit', $regular->id), false);
|
|
$this->withExceptionHandling()->get(route('system-settings.users.edit', $this->user->id))->assertForbidden();
|
|
$this->patch(route('system-settings.users.update', $this->user->id), $this->payload($this->user, 'user'))->assertForbidden();
|
|
$this->delete(route('system-settings.users.destroy', $this->user->id))->assertForbidden();
|
|
}
|
|
|
|
public function test_super_admin_can_assign_both_administrator_roles(): void
|
|
{
|
|
$target = $this->account('user');
|
|
$this->loginAs()->get(route('system-settings.users.edit', $target->id))->assertOk()
|
|
->assertSeeText('主管理员(最高权限)')->assertSee('value="admin"', false);
|
|
foreach (['admin', 'super_admin'] as $role) {
|
|
$this->patch(route('system-settings.users.update', $target->id), $this->payload($target, $role))
|
|
->assertRedirect(route('system-settings.users.index'));
|
|
$target = $target->fresh();
|
|
$this->assertSame([$role], $target->roles()->pluck('name')->all());
|
|
}
|
|
setting()->set('system.sidebar_permissions', ['configured' => ['user_'.$target->id], 'subjects' => ['user_'.$target->id => []]]);
|
|
$this->assertSame(BusinessNavigation::all(), SidebarPermissions::navigationFor($target));
|
|
$this->assertTrue(SystemAccess::isSuperAdmin($target));
|
|
$this->loginAs($target)->get(route('system-settings.users.index'))->assertOk();
|
|
$this->get(route('settings.navigation.manage'))->assertOk();
|
|
}
|
|
|
|
public function test_admin_cannot_promote_a_user_or_itself(): void
|
|
{
|
|
$admin = $this->account('admin');
|
|
$target = $this->account('user');
|
|
$this->withExceptionHandling()->loginAs($admin);
|
|
foreach (['admin', 'super_admin'] as $role) {
|
|
$this->patchJson(route('system-settings.users.update', $target->id), $this->payload($target, $role))
|
|
->assertUnprocessable()->assertJsonValidationErrors('role');
|
|
}
|
|
$this->patchJson(route('system-settings.users.update', $admin->id), $this->payload($admin, 'super_admin'))->assertForbidden();
|
|
$this->assertSame(['user'], $target->roles()->pluck('name')->all());
|
|
}
|
|
|
|
public function test_demotion_removes_cached_administrator_access(): void
|
|
{
|
|
$target = $this->account('super_admin');
|
|
$this->assertTrue($target->hasRole('super_admin'));
|
|
$this->assertTrue($target->can('update-auth-users'));
|
|
$this->loginAs()->patch(route('system-settings.users.update', $target->id), $this->payload($target, 'user'))
|
|
->assertRedirect(route('system-settings.users.index'));
|
|
$this->assertSame('user', SystemAccess::roleName($target));
|
|
$this->assertFalse($target->fresh()->can('update-auth-users'));
|
|
$this->withExceptionHandling()->loginAs($target)->get(route('system-settings.users.index'))->assertForbidden();
|
|
}
|
|
|
|
public function test_legacy_user_routes_cannot_grant_administrator_identity(): void
|
|
{
|
|
$admin = $this->account('admin');
|
|
$target = $this->account('user');
|
|
$this->withExceptionHandling()->loginAs($admin);
|
|
$role = (string) Role::where('name', 'super_admin')->value('id');
|
|
$this->patchJson(route('users.update', $target->id), ['roles' => $role])->assertForbidden();
|
|
$this->patchJson(route('profile.update', $admin->id), ['roles' => $role])->assertForbidden();
|
|
$this->postJson(route('users.store'), ['roles' => $role])->assertForbidden();
|
|
$this->patchJson(route('users.update', $this->user->id), ['name' => 'unauthorized'])->assertForbidden();
|
|
}
|
|
|
|
public function test_last_super_admin_cannot_be_demoted_or_disabled(): void
|
|
{
|
|
$this->loginAs();
|
|
$this->patch(route('system-settings.users.update', $this->user->id), $this->payload($this->user, 'admin'))
|
|
->assertSessionHasErrors('role');
|
|
$this->patch(route('system-settings.users.update', $this->user->id), array_merge($this->payload($this->user, 'super_admin'), ['enabled' => 0]))
|
|
->assertSessionHasErrors('enabled');
|
|
$this->assertSame(['super_admin'], $this->user->roles()->pluck('name')->all());
|
|
}
|
|
|
|
public function test_user_page_settings_are_a_second_level_page_and_save_for_selected_user(): void
|
|
{
|
|
$target = $this->account('user');
|
|
$this->loginAs()->get(route('system-settings.permissions.index'))->assertOk()
|
|
->assertSeeText('用户可访问页面')->assertSee(route('system-settings.sidebar.edit'), false);
|
|
$this->get(route('system-settings.sidebar.edit', ['subject' => 'user_'.$target->id]))->assertOk()
|
|
->assertSee('aria-label="页面位置"', false)->assertSeeText('主体商标规则');
|
|
$this->patch(route('system-settings.sidebar.update'), [
|
|
'subject' => 'user_'.$target->id, 'permissions' => ['store-management/basic-information'],
|
|
])->assertRedirect(route('system-settings.sidebar.edit', ['subject' => 'user_'.$target->id]));
|
|
$this->assertSame(['store-management/basic-information'], SidebarPermissions::allowedKeys($target));
|
|
$this->get(route('system-settings.sidebar.edit', ['subject' => 'user_'.$target->id]))->assertOk()->assertSeeText('自定义权限');
|
|
}
|
|
|
|
public function test_super_admin_cannot_be_limited_by_page_settings_and_regular_users_cannot_configure_access(): void
|
|
{
|
|
$this->withExceptionHandling()->loginAs()->patchJson(route('system-settings.sidebar.update'), [
|
|
'subject' => 'user_'.$this->user->id, 'permissions' => [],
|
|
])->assertUnprocessable();
|
|
$regular = $this->account('user');
|
|
$this->loginAs($regular)->get(route('system-settings.permissions.index'))->assertForbidden();
|
|
$this->get(route('system-settings.users.index'))->assertForbidden();
|
|
$this->patchJson(route('system-settings.sidebar.update'), ['subject' => 'role_user', 'permissions' => []])->assertForbidden();
|
|
}
|
|
|
|
public function test_child_page_navigation_contains_only_the_selected_child(): void
|
|
{
|
|
$regular = $this->account('user');
|
|
setting()->set('system.sidebar_permissions', ['configured' => ['user_'.$regular->id], 'subjects' => ['user_'.$regular->id => ['trademark-management/subject-trademark-rules']]]);
|
|
$navigation = SidebarPermissions::navigationFor($regular);
|
|
$pages = collect($navigation)->flatMap(fn ($section) => $section['pages'])->flatMap(fn ($page) => $page['children'] ?? [$page]);
|
|
$this->assertSame(['subject-trademark-rules'], $pages->pluck('slug')->all());
|
|
}
|
|
|
|
public function test_dingtalk_bound_user_cannot_be_deleted_into_an_orphaned_identity(): void
|
|
{
|
|
$target = $this->account('user');
|
|
DingTalkIdentity::query()->create([
|
|
'user_id' => $target->id,
|
|
'union_id' => 'fixture-delete-guard-union',
|
|
'dingtalk_user_id' => 'fixture-delete-guard-user',
|
|
'nickname' => '在职财务',
|
|
]);
|
|
|
|
$this->loginAs()->delete(route('system-settings.users.destroy', $target->id))
|
|
->assertSessionHasErrors('user');
|
|
$this->assertNotNull($target->fresh());
|
|
$this->assertDatabaseHas('dingtalk_identities', ['user_id' => $target->id]);
|
|
}
|
|
|
|
private function account(string $role): User
|
|
{
|
|
$name = 'fixture-'.$role.'-'.User::count();
|
|
$account = User::create(['name' => $name, 'email' => $name.'@example.test', 'password' => 'fixture-unused-password', 'enabled' => true, 'locale' => 'zh-CN', 'landing_page' => 'dashboard']);
|
|
$account->roles()->attach(Role::where('name', $role)->firstOrFail()->id);
|
|
$account->permissions()->attach(Permission::where('name', 'read-admin-panel')->firstOrFail()->id);
|
|
$account->companies()->attach($this->company->id);
|
|
return $account;
|
|
}
|
|
|
|
private function payload(User $target, string $role): array
|
|
{
|
|
return ['name' => $target->name, 'email' => $target->email, 'role' => $role, 'enabled' => 1];
|
|
}
|
|
}
|