Files
HeuJung 71abdeaf60 fix(security): KVE-2026-1914/1915/1919 remediation 전건
위임 관리자(부관리자)가 권한·역할·표현식·비밀 콘텐츠 경계를 우회하던
결함군을 계층 대칭성 원칙으로 전건 차단한다. 약한 경로가 정상 응답을
내보내는 것이 유일한 증상이라, 게이트를 생산 지점 한 곳(SSoT)에 두고
같은 데이터를 내보내는 소비 경로 전부가 그 게이트를 경유하도록 맞췄다.

- 등급 상한(rank ceiling): 슈퍼관리자 보호·역할/사용자 역할 배정(추가·제거
 대칭)·일괄 상태변경·순서변경을 상세 경로와 동일 강도로 재적용. 가드는
 DB 쓰기에 선행하여 거부 시 상태 불변.
- 레이아웃 표현식: new Function/with 실행을 AST 화이트리스트 평가기로 교체.
 비-문자열 computed 키 정규화(normalizeKey)·Object facade(리플렉션 static
 제거)·legacy 접근자 차단. 저장측 검증·정적 검사와 3계층 동형.
- secret 게이트: 비밀글의 댓글·첨부·문의 독립 경로 재적용, hash 파일서빙
 소유권·비밀·발행 상태 검사 통일.
- 신뢰 스크립트 호스트: 확장 선언 기반 + same-origin 브라우저 정규화를
 런타임·저장측·정적검사 3층 동형화.
- 회귀 감지: 단위·Feature·E2E·시나리오 매니페스트 전축 + audit 룰 4종 신설.
2026-08-17 01:45:09 +09:00

773 lines
28 KiB
PHP

<?php
namespace Tests\Unit\Services;
use App\Enums\ExtensionOwnerType;
use App\Enums\PermissionType;
use App\Exceptions\CannotDeleteSuperAdminException;
use App\Exceptions\PermissionEscalationException;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Models\UserConsent;
use App\Services\UserService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
/**
* UserService 테스트
*
* UserService의 사용자 삭제 및 슈퍼 관리자 관련 기능을 검증합니다.
*/
class UserServiceTest extends TestCase
{
use RefreshDatabase;
private UserService $userService;
protected function setUp(): void
{
parent::setUp();
$this->userService = app(UserService::class);
}
// ========================================================================
// deleteUser() - 슈퍼 관리자 삭제 방지 테스트
// ========================================================================
/**
* 슈퍼 관리자 삭제 시 CannotDeleteSuperAdminException 발생 확인
*/
public function test_delete_super_admin_throws_cannot_delete_super_admin_exception(): void
{
$superAdmin = User::factory()->create(['is_super' => true]);
$this->expectException(CannotDeleteSuperAdminException::class);
$this->userService->deleteUser($superAdmin);
}
/**
* 슈퍼 관리자 삭제 시 예외 메시지가 올바른지 확인
*/
public function test_cannot_delete_super_admin_exception_has_correct_message(): void
{
$superAdmin = User::factory()->create(['is_super' => true]);
try {
$this->userService->deleteUser($superAdmin);
$this->fail('Expected CannotDeleteSuperAdminException was not thrown');
} catch (CannotDeleteSuperAdminException $e) {
// 예외 메시지가 다국어 키로 번역되는지 확인
$this->assertNotEmpty($e->getMessage());
}
}
// ========================================================================
// deleteUser() - 관리자 삭제: 권한/스코프 기반 위임 (하드코딩 차단 제거)
// ========================================================================
/**
* 관리자 권한을 가진 사용자(target=admin) 도 UserService::deleteUser 단독으로는 삭제 가능해야 한다.
*
* 회귀: "target.isAdmin() → CannotDeleteAdminException" 하드코딩이 G7 의 역할/퍼미션/스코프 시스템을
* 우회해 슈퍼관리자도 다른 관리자를 삭제하지 못하도록 막던 결함. 권한/스코프 검증은 미들웨어
* (PermissionMiddleware) 가 담당하고, 시스템 불변식인 슈퍼관리자 보호만 Service 가 보장한다.
*/
public function test_delete_admin_user_succeeds_when_actor_authorized(): void
{
$adminPermission = Permission::create([
'identifier' => 'core.admin.test',
'name' => ['ko' => '관리자 권한', 'en' => 'Admin Permission'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$role = Role::create([
'identifier' => 'test-admin',
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
]);
$role->permissions()->attach($adminPermission->id);
$adminUser = User::factory()->create(['is_super' => false]);
$adminUser->roles()->attach($role->id);
$result = $this->userService->deleteUser($adminUser);
$this->assertTrue($result);
$this->assertDatabaseMissing('users', ['id' => $adminUser->id]);
}
// ========================================================================
// deleteUser() - 일반 사용자 삭제 테스트
// ========================================================================
/**
* 일반 사용자는 정상적으로 삭제 가능
*/
public function test_delete_regular_user_succeeds(): void
{
$regularUser = User::factory()->create(['is_super' => false]);
$result = $this->userService->deleteUser($regularUser);
$this->assertTrue($result);
$this->assertDatabaseMissing('users', ['id' => $regularUser->id]);
}
/**
* user 타입 권한만 가진 사용자는 삭제 가능
*/
public function test_delete_user_with_only_user_type_permissions_succeeds(): void
{
// user 타입 권한 생성
$userPermission = Permission::create([
'identifier' => 'frontend.user.test',
'name' => ['ko' => '사용자 권한', 'en' => 'User Permission'],
'type' => PermissionType::User,
]);
// 역할 생성 및 권한 연결
$role = Role::create([
'identifier' => 'test-user',
'name' => ['ko' => '테스트 사용자', 'en' => 'Test User'],
]);
$role->permissions()->attach($userPermission->id);
// 사용자 생성 및 역할 연결
$user = User::factory()->create(['is_super' => false]);
$user->roles()->attach($role->id);
$result = $this->userService->deleteUser($user);
$this->assertTrue($result);
$this->assertDatabaseMissing('users', ['id' => $user->id]);
}
/**
* 역할이 없는 사용자도 삭제 가능
*/
public function test_delete_user_without_roles_succeeds(): void
{
$userWithoutRoles = User::factory()->create(['is_super' => false]);
// 역할이 없는지 확인
$this->assertEquals(0, $userWithoutRoles->roles()->count());
$result = $this->userService->deleteUser($userWithoutRoles);
$this->assertTrue($result);
$this->assertDatabaseMissing('users', ['id' => $userWithoutRoles->id]);
}
// ========================================================================
// 삭제 우선순위 테스트 (슈퍼 관리자 > 관리자)
// ========================================================================
/**
* 슈퍼 관리자이면서 관리자 권한도 있는 경우, 슈퍼 관리자 예외가 우선
*/
public function test_super_admin_exception_takes_priority_over_admin_exception(): void
{
// admin 타입 권한 생성
$adminPermission = Permission::create([
'identifier' => 'core.priority.test',
'name' => ['ko' => '우선순위 테스트', 'en' => 'Priority Test'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
// 역할 생성 및 권한 연결
$role = Role::create([
'identifier' => 'super-admin-role',
'name' => ['ko' => '슈퍼관리자 역할', 'en' => 'Super Admin Role'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
]);
$role->permissions()->attach($adminPermission->id);
// 슈퍼 관리자이면서 관리자 역할도 가진 사용자
$superAdminWithRole = User::factory()->create(['is_super' => true]);
$superAdminWithRole->roles()->attach($role->id);
// 슈퍼 관리자 예외가 먼저 발생해야 함
$this->expectException(CannotDeleteSuperAdminException::class);
$this->userService->deleteUser($superAdminWithRole);
}
// ========================================================================
// deleteUser() - 관계 레코드 명시적 삭제 검증 (CASCADE 의존 금지)
// ========================================================================
/**
* 사용자 삭제 시 역할 연결이 해제되는지 확인
*/
public function test_delete_user_detaches_roles(): void
{
$role = Role::create([
'identifier' => 'test-detach-role',
'name' => ['ko' => '역할 해제 테스트', 'en' => 'Detach Test'],
]);
$user = User::factory()->create(['is_super' => false]);
$user->roles()->attach($role->id);
$this->assertDatabaseHas('user_roles', ['user_id' => $user->id, 'role_id' => $role->id]);
$this->userService->deleteUser($user);
$this->assertDatabaseMissing('user_roles', ['user_id' => $user->id]);
}
/**
* 사용자 삭제 시 약관 동의 이력이 삭제되는지 확인
*/
public function test_delete_user_deletes_consents(): void
{
$user = User::factory()->create(['is_super' => false]);
UserConsent::create([
'user_id' => $user->id,
'consent_type' => 'terms',
'agreed_at' => now(),
]);
$this->assertDatabaseHas('user_consents', ['user_id' => $user->id]);
$this->userService->deleteUser($user);
$this->assertDatabaseMissing('user_consents', ['user_id' => $user->id]);
}
/**
* 사용자 삭제 시 API 토큰이 삭제되는지 확인
*/
public function test_delete_user_deletes_tokens(): void
{
$user = User::factory()->create(['is_super' => false]);
$user->createToken('test-token');
$this->assertDatabaseHas('personal_access_tokens', [
'tokenable_id' => $user->id,
'tokenable_type' => User::class,
]);
$this->userService->deleteUser($user);
$this->assertDatabaseMissing('personal_access_tokens', [
'tokenable_id' => $user->id,
'tokenable_type' => User::class,
]);
}
// ========================================================================
// createUser() 테스트 (is_super 필드 처리)
// ========================================================================
/**
* 사용자 생성 시 is_super 필드가 정상적으로 저장되는지 확인
*/
public function test_create_user_with_is_super_true(): void
{
$userData = [
'name' => 'Super Admin Test',
'email' => 'superadmin@test.com',
'password' => 'password123',
'is_super' => true,
];
$user = $this->userService->createUser($userData);
$this->assertTrue($user->is_super);
$this->assertTrue($user->isSuperAdmin());
}
/**
* 사용자 생성 시 is_super가 기본값 false인지 확인
*/
#[Test]
public function test_create_user_is_super_defaults_to_false(): void
{
$userData = [
'name' => 'Regular User Test',
'email' => 'regularuser@test.com',
'password' => 'password123',
];
$user = $this->userService->createUser($userData);
$this->assertFalse($user->is_super);
$this->assertFalse($user->isSuperAdmin());
}
// ========================================================================
// updateUser() 테스트 (is_super 필드 처리)
// ========================================================================
/**
* 사용자 업데이트 시 is_super 필드 변경 가능
*/
public function test_update_user_can_change_is_super(): void
{
$user = User::factory()->create(['is_super' => false]);
$this->assertFalse($user->is_super);
$updatedUser = $this->userService->updateUser($user, ['is_super' => true]);
$this->assertTrue($updatedUser->is_super);
$this->assertTrue($updatedUser->isSuperAdmin());
}
/**
* 슈퍼 관리자를 일반 사용자로 강등 가능
*/
public function test_update_user_can_demote_super_admin(): void
{
$superAdmin = User::factory()->create(['is_super' => true]);
$this->assertTrue($superAdmin->isSuperAdmin());
$updatedUser = $this->userService->updateUser($superAdmin, ['is_super' => false]);
$this->assertFalse($updatedUser->is_super);
$this->assertFalse($updatedUser->isSuperAdmin());
}
// ========================================================================
// updateUser() — 역할 할당 권한 체크 테스트
// ========================================================================
/**
* core.permissions.update 권한이 있으면 자기 자신의 역할도 변경 가능
*/
public function test_update_user_can_change_own_roles_with_permission(): void
{
// core.permissions.update 권한을 가진 사용자
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'test-perm-admin',
'name' => ['ko' => '권한 관리자', 'en' => 'Permission Admin'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$newRole = Role::create([
'identifier' => 'new-role',
'name' => ['ko' => '새 역할', 'en' => 'New Role'],
]);
$user = User::factory()->create();
$user->roles()->attach($adminRole->id);
// 자기 자신으로 인증
Auth::login($user);
// 자기 자신의 역할 변경 시도 (admin 역할이 아니므로 자기잠금 방지 해당 없음)
$updatedUser = $this->userService->updateUser($user, [
'name' => 'Updated Name',
'role_ids' => [$adminRole->id, $newRole->id],
]);
// 이름과 역할 모두 변경되어야 함
$this->assertEquals('Updated Name', $updatedUser->name);
$this->assertTrue($updatedUser->roles->contains('id', $adminRole->id));
$this->assertTrue($updatedUser->roles->contains('id', $newRole->id));
}
/**
* 마지막 admin 역할 사용자가 자기 admin 역할을 제거하려는 경우 차단
*/
public function test_update_user_prevents_last_admin_role_removal(): void
{
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'admin',
'name' => ['ko' => '관리자', 'en' => 'Admin'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$otherRole = Role::create([
'identifier' => 'other-role',
'name' => ['ko' => '다른 역할', 'en' => 'Other Role'],
]);
// 유일한 admin 사용자
$user = User::factory()->create();
$user->roles()->attach($adminRole->id);
Auth::login($user);
// admin 역할을 제거하고 다른 역할만 남기려는 시도
$this->expectException(ValidationException::class);
$this->userService->updateUser($user, [
'role_ids' => [$otherRole->id],
]);
}
/**
* admin 역할을 가진 다른 사용자가 있으면 자기 admin 역할 제거 가능
*/
public function test_update_user_allows_admin_role_removal_when_other_admins_exist(): void
{
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'admin',
'name' => ['ko' => '관리자', 'en' => 'Admin'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$otherRole = Role::create([
'identifier' => 'other-role-2',
'name' => ['ko' => '다른 역할', 'en' => 'Other Role'],
]);
// admin 역할을 가진 사용자 2명
$user = User::factory()->create();
$user->roles()->attach($adminRole->id);
$otherAdmin = User::factory()->create();
$otherAdmin->roles()->attach($adminRole->id);
Auth::login($user);
// 다른 admin이 있으므로 자기 admin 역할 제거 가능
$updatedUser = $this->userService->updateUser($user, [
'role_ids' => [$otherRole->id],
]);
$this->assertFalse($updatedUser->roles->contains('id', $adminRole->id));
$this->assertTrue($updatedUser->roles->contains('id', $otherRole->id));
}
/**
* 마지막 admin 역할 제거 시 ValidationException에 role_ids 에러가 포함되는지 확인
*/
public function test_last_admin_role_removal_has_role_ids_error_key(): void
{
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'admin',
'name' => ['ko' => '관리자', 'en' => 'Admin'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$otherRole = Role::create([
'identifier' => 'user-role',
'name' => ['ko' => '사용자', 'en' => 'User'],
]);
$user = User::factory()->create();
$user->roles()->attach($adminRole->id);
Auth::login($user);
try {
$this->userService->updateUser($user, [
'role_ids' => [$otherRole->id],
]);
$this->fail('Expected ValidationException was not thrown');
} catch (ValidationException $e) {
$this->assertArrayHasKey('role_ids', $e->errors());
$this->assertNotEmpty($e->errors()['role_ids']);
}
}
/**
* 역할 부여는 core.permissions.update(역할 정의 수정)를 요구하지 않는다 — 상한(ceiling)
* 이내라면 그 권한이 없어도 역할을 부여할 수 있다. (역할 부여 = 사용자 관리의 일부)
*
* 회귀 방지: 과거에는 core.permissions.update 가 없으면 role_ids 를 조용히 버려(silent
* no-op) 200 성공을 반환하면서도 역할이 바뀌지 않았다. 이제는 상한만 통과하면 부여된다.
*/
public function test_update_user_assigns_role_within_ceiling_without_permissions_update(): void
{
$originalRole = Role::create([
'identifier' => 'original-role',
'name' => ['ko' => '원래 역할', 'en' => 'Original Role'],
]);
// 권한이 전혀 없는 역할 → 어떤 액터의 상한도 넘지 않는다.
$newRole = Role::create([
'identifier' => 'new-role-test',
'name' => ['ko' => '새 역할', 'en' => 'New Role'],
]);
$targetUser = User::factory()->create();
$targetUser->roles()->attach($originalRole->id);
// core.permissions.update 가 없는 액터로 인증 (사용자 관리 권한은 라우트 단계에서
// 강제되며, 서비스 계층 테스트에서는 상한만 검사된다)
$authUser = User::factory()->create();
Auth::login($authUser);
$updatedUser = $this->userService->updateUser($targetUser, [
'name' => 'Changed Name',
'role_ids' => [$newRole->id],
]);
// 이름과 역할 모두 변경되어야 한다 (silent no-op 아님)
$this->assertEquals('Changed Name', $updatedUser->name);
$this->assertTrue($updatedUser->roles->contains('id', $newRole->id));
$this->assertFalse($updatedUser->roles->contains('id', $originalRole->id));
}
/**
* 상한을 넘는 역할(액터가 보유하지 않은 권한을 담은 역할)은 부여 시 명시적으로 거부된다.
*
* 회귀 방지: 조용히 무시(silent no-op)하는 대신 PermissionEscalationException 을 던져
* 컨트롤러가 403 으로 매핑하도록 한다.
*/
public function test_update_user_rejects_role_beyond_actor_ceiling(): void
{
$elevatedPermission = Permission::create([
'identifier' => 'core.templates.uninstall',
'name' => ['ko' => '템플릿 삭제', 'en' => 'Uninstall Templates'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
// 액터가 보유하지 않은 권한을 담은 역할 → 상한 초과
$elevatedRole = Role::create([
'identifier' => 'elevated-role',
'name' => ['ko' => '상위 역할', 'en' => 'Elevated Role'],
]);
$elevatedRole->permissions()->attach($elevatedPermission->id);
$targetUser = User::factory()->create();
// 그 권한을 보유하지 않은 액터
$authUser = User::factory()->create();
Auth::login($authUser);
$this->expectException(PermissionEscalationException::class);
$this->userService->updateUser($targetUser, [
'role_ids' => [$elevatedRole->id],
]);
}
/**
* core.permissions.update 권한이 있으면 타인의 역할 변경 가능
*/
public function test_update_user_allows_role_change_with_permission(): void
{
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'perm-admin-role',
'name' => ['ko' => '권한 관리 역할', 'en' => 'Permission Admin Role'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$originalRole = Role::create([
'identifier' => 'original-role-2',
'name' => ['ko' => '원래 역할', 'en' => 'Original Role'],
]);
$newRole = Role::create([
'identifier' => 'target-role',
'name' => ['ko' => '대상 역할', 'en' => 'Target Role'],
]);
$targetUser = User::factory()->create();
$targetUser->roles()->attach($originalRole->id);
// 권한 있는 사용자로 인증
$authUser = User::factory()->create();
$authUser->roles()->attach($adminRole->id);
Auth::login($authUser);
// 타인의 역할 변경
$updatedUser = $this->userService->updateUser($targetUser, [
'role_ids' => [$newRole->id],
]);
// 역할이 새 역할로 변경되어야 함
$this->assertTrue($updatedUser->roles->contains('id', $newRole->id));
$this->assertFalse($updatedUser->roles->contains('id', $originalRole->id));
}
// ========================================================================
// createUser() — 역할 할당 권한 체크 테스트
// ========================================================================
/**
* 역할을 지정하지 않고 생성하면 기본 역할('user')이 자동 배정된다.
*
* 회귀 방지: 기본 역할 배정은 권한 게이트가 아니라 "요청 역할 없음" 조건에 묶인다.
* (과거에는 core.permissions.update 미보유 조건에 묶여 있어 지정 역할을 덮어썼다)
*/
public function test_create_user_assigns_default_role_when_none_requested(): void
{
$userRole = Role::create([
'identifier' => 'user',
'name' => ['ko' => '사용자', 'en' => 'User'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'is_active' => true,
]);
$authUser = User::factory()->create();
Auth::login($authUser);
$user = $this->userService->createUser([
'name' => 'Test User',
'email' => 'testcreate@test.com',
'password' => 'password123',
]);
$this->assertTrue($user->roles->contains('id', $userRole->id));
}
/**
* core.permissions.update 가 없어도 상한 이내의 지정 역할은 그대로 부여된다
* (기본 역할로 덮어쓰지 않는다).
*
* 회귀 방지: 과거에는 core.permissions.update 미보유 시 지정 역할을 버리고 기본 역할을
* 강제 배정했다. 이제는 상한만 통과하면 지정 역할이 부여된다.
*/
public function test_create_user_assigns_requested_role_within_ceiling_without_permissions_update(): void
{
$userRole = Role::create([
'identifier' => 'user',
'name' => ['ko' => '사용자', 'en' => 'User'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'is_active' => true,
]);
// 권한이 없는 역할 → 상한 이내
$customRole = Role::create([
'identifier' => 'custom-role',
'name' => ['ko' => '커스텀 역할', 'en' => 'Custom Role'],
]);
$authUser = User::factory()->create();
Auth::login($authUser);
$user = $this->userService->createUser([
'name' => 'Test User',
'email' => 'testcreate@test.com',
'password' => 'password123',
'role_ids' => [$customRole->id],
]);
// 지정 역할이 부여되고 기본 역할로 덮어쓰이지 않는다
$this->assertTrue($user->roles->contains('id', $customRole->id));
$this->assertFalse($user->roles->contains('id', $userRole->id));
}
/**
* 상한을 넘는 역할을 지정해 생성하면 명시적으로 거부된다.
*/
public function test_create_user_rejects_role_beyond_actor_ceiling(): void
{
$elevatedPermission = Permission::create([
'identifier' => 'core.templates.uninstall',
'name' => ['ko' => '템플릿 삭제', 'en' => 'Uninstall Templates'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$elevatedRole = Role::create([
'identifier' => 'elevated-create-role',
'name' => ['ko' => '상위 역할', 'en' => 'Elevated Role'],
]);
$elevatedRole->permissions()->attach($elevatedPermission->id);
$authUser = User::factory()->create();
Auth::login($authUser);
$this->expectException(PermissionEscalationException::class);
$this->userService->createUser([
'name' => 'New User',
'email' => 'newuser@test.com',
'password' => 'password123',
'role_ids' => [$elevatedRole->id],
]);
}
/**
* core.permissions.update 권한이 있으면 지정한 역할 할당 가능
*/
public function test_create_user_assigns_specified_roles_with_permission(): void
{
$permUpdatePermission = Permission::create([
'identifier' => 'core.permissions.update',
'name' => ['ko' => '권한 수정', 'en' => 'Update Permissions'],
'extension_type' => ExtensionOwnerType::Core,
'extension_identifier' => 'core',
'type' => PermissionType::Admin,
]);
$adminRole = Role::create([
'identifier' => 'create-perm-admin',
'name' => ['ko' => '권한 관리자', 'en' => 'Permission Admin'],
]);
$adminRole->permissions()->attach($permUpdatePermission->id);
$customRole = Role::create([
'identifier' => 'assigned-role',
'name' => ['ko' => '할당 역할', 'en' => 'Assigned Role'],
]);
// 권한 있는 사용자로 인증
$authUser = User::factory()->create();
$authUser->roles()->attach($adminRole->id);
Auth::login($authUser);
$user = $this->userService->createUser([
'name' => 'New User',
'email' => 'newuser@test.com',
'password' => 'password123',
'role_ids' => [$customRole->id],
]);
// 지정한 역할이 할당되어야 함
$this->assertTrue($user->roles->contains('id', $customRole->id));
}
}