180 lines
11 KiB
PHP
180 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\SystemSettings;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use App\Models\Auth\Role;
|
|
use App\Models\Auth\User;
|
|
use App\Models\Auth\UserAccessGroup;
|
|
use App\Utilities\SidebarPermissions;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class UserAccessGroupsTest extends FeatureTestCase
|
|
{
|
|
public function test_create_group_and_add_members_without_granting_administrator_identity(): void
|
|
{
|
|
$member = $this->member();
|
|
$this->loginAs()->post(route('system-settings.groups.store'), ['name' => '核算组', 'members' => [$member->id]])
|
|
->assertRedirect(route('system-settings.groups.index'));
|
|
$group = UserAccessGroup::where('name', '核算组')->sole();
|
|
$this->assertSame([], $group->page_permissions);
|
|
$this->assertSame([], SidebarPermissions::allowedKeys($member));
|
|
$this->assertSame(['user'], $member->roles()->pluck('name')->all());
|
|
$this->get(route('system-settings.users.index'))->assertOk()->assertSeeText('核算组');
|
|
$this->get(route('system-settings.groups.index'))->assertOk()->assertSeeText('未开放页面');
|
|
}
|
|
|
|
public function test_later_user_can_be_added_to_existing_groups_from_user_management(): void
|
|
{
|
|
$first = $this->member();
|
|
$group = $this->group('现有核算组', $first, ['store-management/basic-information']);
|
|
$group->users()->detach($first->id);
|
|
$later = $this->member();
|
|
|
|
$this->loginAs()->get(route('system-settings.users.index'))->assertOk()
|
|
->assertSee(route('system-settings.groups.membership.edit', $later->id), false)
|
|
->assertSeeText('管理分组');
|
|
$this->get(route('system-settings.groups.membership.edit', $later->id))->assertOk()
|
|
->assertSeeText('现有核算组')->assertSeeText($later->name);
|
|
$this->patch(route('system-settings.groups.membership.update', $later->id), ['groups' => [$group->id]])
|
|
->assertRedirect(route('system-settings.users.index'));
|
|
|
|
$this->assertTrue($group->users()->whereKey($later->id)->exists());
|
|
$this->assertSame(['store-management/basic-information'], SidebarPermissions::allowedKeys($later));
|
|
}
|
|
|
|
public function test_user_membership_editor_is_company_scoped_and_super_admin_only(): void
|
|
{
|
|
$member = $this->member();
|
|
$group = $this->group('本公司分组', $member);
|
|
$admin = $this->member('admin');
|
|
|
|
$this->withExceptionHandling()->loginAs($admin)
|
|
->get(route('system-settings.groups.membership.edit', $member->id))->assertForbidden();
|
|
$this->loginAs()->withHeaders(['X-Requested-With' => 'XMLHttpRequest'])
|
|
->get(route('system-settings.groups.membership.edit', $this->user->id))->assertNotFound();
|
|
$this->patchJson(route('system-settings.groups.membership.update', $member->id), ['groups' => [$group->id + 9999]])
|
|
->assertUnprocessable();
|
|
$this->assertSame([$group->id], UserAccessGroup::forUser($member)->pluck('id')->all());
|
|
}
|
|
|
|
public function test_multiple_group_permissions_are_unioned_and_enforced_on_real_routes(): void
|
|
{
|
|
$member = $this->member();
|
|
$a = $this->group('核算', $member, ['store-management/basic-information']);
|
|
$b = $this->group('复核', $member, ['trademark-management/information']);
|
|
$this->assertEqualsCanonicalizing(['store-management/basic-information', 'trademark-management/information'], SidebarPermissions::allowedKeys($member));
|
|
$this->withExceptionHandling()->loginAs($member)->get(route('business.pages.show', ['section' => 'company-management', 'page' => 'basic-information']))->assertForbidden();
|
|
$this->loginAs()->patch(route('system-settings.sidebar.update'), ['subject' => 'group_'.$b->id, 'permissions' => []])
|
|
->assertRedirect(route('system-settings.sidebar.edit', ['subject' => 'group_'.$b->id]));
|
|
$this->assertSame(['store-management/basic-information'], SidebarPermissions::allowedKeys($member));
|
|
$this->loginAs($member)->get(route('dashboard'))->assertRedirectContains('/workspace/store-management/basic-information');
|
|
}
|
|
|
|
public function test_user_override_can_be_empty_and_can_restore_group_inheritance(): void
|
|
{
|
|
$member = $this->member();
|
|
$this->group('共享核算组', $member, ['store-management/basic-information']);
|
|
$this->loginAs()->patch(route('system-settings.sidebar.update'), ['subject' => 'user_'.$member->id, 'permissions' => []]);
|
|
$this->assertSame([], SidebarPermissions::allowedKeys($member));
|
|
$this->get(route('system-settings.sidebar.edit', ['subject' => 'user_'.$member->id]))->assertOk()->assertSeeText('恢复继承分组权限');
|
|
$this->patch(route('system-settings.sidebar.update'), ['subject' => 'user_'.$member->id, 'mode' => 'inherit'])
|
|
->assertRedirect(route('system-settings.sidebar.edit', ['subject' => 'user_'.$member->id]));
|
|
$this->assertSame(['store-management/basic-information'], SidebarPermissions::allowedKeys($member));
|
|
$this->get(route('system-settings.sidebar.edit', ['subject' => 'user_'.$member->id]))->assertOk()->assertSeeText('正在继承分组:共享核算组');
|
|
}
|
|
|
|
public function test_group_configuration_uses_the_existing_permissions_editor(): void
|
|
{
|
|
$member = $this->member();
|
|
$group = $this->group('出纳组', $member);
|
|
$this->loginAs()->get(route('system-settings.sidebar.edit', ['subject' => 'group_'.$group->id]))->assertOk()->assertSeeText('分组:出纳组');
|
|
$this->patch(route('system-settings.sidebar.update'), ['subject' => 'group_'.$group->id, 'permissions' => ['fund-management']]);
|
|
$this->assertSame(['fund-management'], $group->fresh()->page_permissions);
|
|
$this->assertSame(['fund-management'], SidebarPermissions::allowedKeys($member));
|
|
$this->withExceptionHandling()->patchJson(route('system-settings.sidebar.update'), ['subject' => 'group_'.$group->id, 'permissions' => ['not-a-page']])->assertUnprocessable();
|
|
}
|
|
|
|
public function test_group_page_access_does_not_grant_store_password_access(): void
|
|
{
|
|
$member = $this->member();
|
|
$this->group('店铺资料组', $member, ['store-management']);
|
|
$this->mock(\App\Services\StoreAccountPasswordClient::class)->shouldNotReceive('reveal');
|
|
$this->withExceptionHandling()->loginAs($member)->postJson(route('business.store-account-passwords.reveal', ['account' => 'fixture-account']), ['purpose' => 'view'])->assertForbidden();
|
|
}
|
|
|
|
public function test_group_membership_and_names_can_change_without_changing_permissions(): void
|
|
{
|
|
$member = $this->member();
|
|
$other = $this->member();
|
|
$group = $this->group('旧名称', $member, ['store-management']);
|
|
$this->loginAs()->put(route('system-settings.groups.update', $group->id), ['name' => '新名称', 'members' => [$other->id]])
|
|
->assertRedirect(route('system-settings.groups.index'));
|
|
$this->assertSame(['store-management'], SidebarPermissions::allowedKeys($other));
|
|
$this->assertNull(SidebarPermissions::allowedKeys($member));
|
|
$this->assertSame(['store-management'], $group->fresh()->page_permissions);
|
|
$this->assertSame('新名称', $group->fresh()->name);
|
|
}
|
|
|
|
public function test_delete_requires_an_empty_group(): void
|
|
{
|
|
$member = $this->member();
|
|
$group = $this->group('核算组', $member);
|
|
$this->loginAs()->delete(route('system-settings.groups.destroy', $group->id))->assertSessionHasErrors('group');
|
|
$this->assertNotNull($group->fresh());
|
|
$this->put(route('system-settings.groups.update', $group->id), ['name' => '核算组']);
|
|
$this->delete(route('system-settings.groups.destroy', $group->id))->assertRedirect(route('system-settings.groups.index'));
|
|
$this->assertNull($group->fresh());
|
|
}
|
|
|
|
public function test_only_super_admins_can_change_groups_or_group_permissions(): void
|
|
{
|
|
$member = $this->member('admin');
|
|
$group = $this->group('管理员业务组', $member);
|
|
$this->withExceptionHandling()->loginAs($member)->get(route('system-settings.groups.index'))->assertForbidden();
|
|
$this->post(route('system-settings.groups.store'), ['name' => '不允许'])->assertForbidden();
|
|
$this->put(route('system-settings.groups.update', $group->id), ['name' => '不允许'])->assertForbidden();
|
|
$this->delete(route('system-settings.groups.destroy', $group->id))->assertForbidden();
|
|
$this->patchJson(route('system-settings.sidebar.update'), ['subject' => 'group_'.$group->id, 'permissions' => ['store-management']])->assertUnprocessable();
|
|
}
|
|
|
|
public function test_group_and_member_ids_cannot_cross_company_or_grant_super_admin_membership(): void
|
|
{
|
|
$member = $this->member();
|
|
$member->companies()->detach();
|
|
$this->withExceptionHandling()->loginAs()->postJson(route('system-settings.groups.store'), ['name' => '跨公司', 'members' => [$member->id]])->assertUnprocessable();
|
|
$this->postJson(route('system-settings.groups.store'), ['name' => '主管理员组', 'members' => [$this->user->id]])->assertUnprocessable();
|
|
$group = $this->group('本公司', $this->member());
|
|
$this->withHeaders(['X-Requested-With' => 'XMLHttpRequest'])->getJson(route('system-settings.groups.edit', $group->id + 9999))->assertNotFound();
|
|
$otherCompany = $this->company->replicate();
|
|
$otherCompany->domain = 'group-isolation.example.test';
|
|
$otherCompany->save();
|
|
$foreignGroup = new UserAccessGroup(['name' => '其他公司分组', 'page_permissions' => ['store-management']]);
|
|
$foreignGroup->company_id = $otherCompany->id;
|
|
$foreignGroup->save();
|
|
$this->getJson(route('system-settings.groups.edit', $foreignGroup->id))->assertNotFound();
|
|
$this->patchJson(route('system-settings.sidebar.update'), ['subject' => 'group_'.$foreignGroup->id, 'permissions' => []])->assertUnprocessable();
|
|
$this->postJson(route('system-settings.groups.store'), ['name' => '本公司'])->assertUnprocessable();
|
|
$this->assertNull(SidebarPermissions::allowedKeys($this->user));
|
|
}
|
|
|
|
private function member(string $role = 'user'): User
|
|
{
|
|
$name = 'member-'.User::count();
|
|
$user = User::create(['name' => $name, 'email' => $name.'@example.test', 'password' => 'fixture-only-password', 'enabled' => true, 'locale' => 'zh-CN', 'landing_page' => 'dashboard']);
|
|
$user->roles()->attach(Role::where('name', $role)->sole()->id);
|
|
$user->permissions()->attach(Permission::where('name', 'read-admin-panel')->sole()->id);
|
|
$user->companies()->attach($this->company->id);
|
|
return $user;
|
|
}
|
|
|
|
private function group(string $name, User $member, array $permissions = []): UserAccessGroup
|
|
{
|
|
$group = new UserAccessGroup(['name' => $name, 'page_permissions' => $permissions]);
|
|
$group->company_id = $this->company->id;
|
|
$group->save();
|
|
$group->users()->attach($member->id);
|
|
return $group;
|
|
}
|
|
}
|