fix: 개인 권한 관리 페이지 메뉴 리스트 표시 오류 수정

- getMenuTree()에 TenantScope 비활성화 추가 (HQ 관리자가 다른 테넌트 메뉴 조회 가능)
- getRolePermissions()에 user_roles 테이블 쿼리 추가 (테넌트별 역할 권한 반영)
- hasRolePermission(), getUserPermissionCounts()도 user_roles 포함하도록 수정
- 사용자 버튼의 data-context-menu를 아이디 뱃지로 이동 (클릭 이벤트 충돌 해결)
This commit is contained in:
2025-12-09 18:43:44 +09:00
parent 82b9ac0ce3
commit 8c348f2e02
2 changed files with 77 additions and 16 deletions

View File

@@ -4,6 +4,7 @@
use App\Models\Commons\Menu;
use App\Models\Permission;
use App\Models\Scopes\TenantScope;
use App\Models\User;
use Illuminate\Support\Facades\DB;
@@ -28,8 +29,8 @@ public function getUserPermissionMatrix(int $userId, ?int $tenantId = null, stri
{
$now = now();
// 1. 역할 권한 조회 (Spatie)
$rolePermissions = $this->getRolePermissions($userId, $guardName);
// 1. 역할 권한 조회 (Spatie + user_roles)
$rolePermissions = $this->getRolePermissions($userId, $guardName, $tenantId);
// 2. 부서 권한 조회 (permission_overrides with Department)
$departmentPermissions = $this->getDepartmentPermissions($userId, $tenantId, $guardName);
@@ -93,11 +94,12 @@ public function getUserPermissionMatrix(int $userId, ?int $tenantId = null, stri
}
/**
* 역할 권한 조회 (Spatie model_has_roles + role_has_permissions)
* 역할 권한 조회 (Spatie model_has_roles + user_roles + role_has_permissions)
*/
private function getRolePermissions(int $userId, string $guardName): array
private function getRolePermissions(int $userId, string $guardName, ?int $tenantId = null): array
{
$rolePermissions = DB::table('model_has_roles as mhr')
// 1. Spatie model_has_roles 테이블에서 권한 조회
$spatiePermissions = DB::table('model_has_roles as mhr')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('mhr.model_type', User::class)
@@ -107,6 +109,24 @@ private function getRolePermissions(int $userId, string $guardName): array
->pluck('p.name')
->toArray();
// 2. user_roles 테이블에서 권한 조회 (테넌트별 역할)
$userRolesQuery = DB::table('user_roles as ur')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'ur.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('ur.user_id', $userId)
->whereNull('ur.deleted_at')
->where('p.guard_name', $guardName)
->where('p.name', 'like', 'menu:%');
if ($tenantId) {
$userRolesQuery->where('ur.tenant_id', $tenantId);
}
$userRolesPermissions = $userRolesQuery->pluck('p.name')->toArray();
// 3. 권한 통합 (중복 제거)
$rolePermissions = array_unique(array_merge($spatiePermissions, $userRolesPermissions));
$result = [];
foreach ($rolePermissions as $permName) {
if (preg_match('/^menu:(\d+)\.(\w+)$/', $permName, $matches)) {
@@ -258,7 +278,7 @@ public function togglePermission(int $userId, int $menuId, string $permissionTyp
->first();
// 역할/부서 권한 확인
$hasRolePermission = $this->hasRolePermission($userId, $permissionName, $guardName);
$hasRolePermission = $this->hasRolePermission($userId, $permissionName, $tenantId, $guardName);
$hasDeptPermission = $this->hasDeptPermission($userId, $permissionName, $tenantId, $guardName);
$hasInheritedPermission = $hasRolePermission || $hasDeptPermission;
@@ -303,11 +323,12 @@ public function togglePermission(int $userId, int $menuId, string $permissionTyp
}
/**
* 역할 권한 존재 여부 확인
* 역할 권한 존재 여부 확인 (Spatie + user_roles)
*/
private function hasRolePermission(int $userId, string $permissionName, string $guardName): bool
private function hasRolePermission(int $userId, string $permissionName, ?int $tenantId, string $guardName): bool
{
return DB::table('model_has_roles as mhr')
// 1. Spatie model_has_roles 테이블에서 확인
$hasSpatiePermission = DB::table('model_has_roles as mhr')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('mhr.model_type', User::class)
@@ -315,6 +336,25 @@ private function hasRolePermission(int $userId, string $permissionName, string $
->where('p.guard_name', $guardName)
->where('p.name', $permissionName)
->exists();
if ($hasSpatiePermission) {
return true;
}
// 2. user_roles 테이블에서 확인
$userRolesQuery = DB::table('user_roles as ur')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'ur.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('ur.user_id', $userId)
->whereNull('ur.deleted_at')
->where('p.guard_name', $guardName)
->where('p.name', $permissionName);
if ($tenantId) {
$userRolesQuery->where('ur.tenant_id', $tenantId);
}
return $userRolesQuery->exists();
}
/**
@@ -616,7 +656,10 @@ public function resetToDefaultPermissions(int $userId, ?int $tenantId = null, st
*/
public function getMenuTree(?int $tenantId = null): \Illuminate\Support\Collection
{
$query = Menu::with('parent')
// TenantScope를 비활성화하고 명시적으로 tenant_id 필터 적용
// (HQ 관리자가 다른 테넌트의 메뉴를 조회할 수 있도록)
$query = Menu::withoutGlobalScope(TenantScope::class)
->with('parent')
->where('is_active', 1);
if ($tenantId) {
@@ -759,8 +802,8 @@ private function getUserPermissionCounts(int $userId, int $tenantId, $now): arra
$result = ['web' => 0, 'api' => 0];
foreach (['web', 'api'] as $guardName) {
// 1. 역할 권한
$rolePermissions = DB::table('model_has_roles as mhr')
// 1-1. Spatie 역할 권한 (model_has_roles)
$spatieRolePermissions = DB::table('model_has_roles as mhr')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('mhr.model_type', User::class)
@@ -770,6 +813,21 @@ private function getUserPermissionCounts(int $userId, int $tenantId, $now): arra
->pluck('p.name')
->toArray();
// 1-2. 테넌트별 역할 권한 (user_roles)
$userRolesPermissions = DB::table('user_roles as ur')
->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'ur.role_id')
->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
->where('ur.user_id', $userId)
->where('ur.tenant_id', $tenantId)
->whereNull('ur.deleted_at')
->where('p.guard_name', $guardName)
->where('p.name', 'like', 'menu:%')
->pluck('p.name')
->toArray();
// 역할 권한 통합
$rolePermissions = array_unique(array_merge($spatieRolePermissions, $userRolesPermissions));
// 2. 부서 권한
$deptPermissions = DB::table('department_user as du')
->join('permission_overrides as po', function ($j) use ($now, $tenantId) {

View File

@@ -40,9 +40,6 @@ class="user-button px-4 py-2 text-sm font-medium rounded-lg border transition-co
data-user-name="{{ $user->name }}"
data-user-login="{{ $user->user_id }}"
data-auto-select="{{ $user->id == $autoSelectId ? 'true' : 'false' }}"
data-context-menu="user"
data-entity-id="{{ $user->id }}"
data-entity-name="{{ $user->name }}"
hx-get="/api/admin/user-permissions/matrix"
hx-target="#permission-matrix"
hx-include="[name='guard_name']"
@@ -50,7 +47,13 @@ class="user-button px-4 py-2 text-sm font-medium rounded-lg border transition-co
onclick="selectUser(this)"
>
{{ $user->name }}
<span class="user-id-badge px-1.5 py-0.5 text-xs bg-gray-200 text-gray-600 rounded">&nbsp;{{ $user->user_id }}&nbsp;</span>
<span
class="user-id-badge px-1.5 py-0.5 text-xs bg-gray-200 text-gray-600 rounded cursor-pointer"
data-context-menu="user"
data-entity-id="{{ $user->id }}"
data-entity-name="{{ $user->name }}"
onclick="event.stopPropagation();"
>&nbsp;{{ $user->user_id }}&nbsp;</span>
@php
$showWebCount = auth()->user()?->is_super_admin && $user->web_permission_count > 0;
$showApiCount = $user->api_permission_count > 0;