diff --git a/app/Services/UserPermissionService.php b/app/Services/UserPermissionService.php
index 87f3195d..1fb6dbff 100644
--- a/app/Services/UserPermissionService.php
+++ b/app/Services/UserPermissionService.php
@@ -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) {
diff --git a/resources/views/user-permissions/index.blade.php b/resources/views/user-permissions/index.blade.php
index 1e25a117..a55e7323 100644
--- a/resources/views/user-permissions/index.blade.php
+++ b/resources/views/user-permissions/index.blade.php
@@ -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 }}
- {{ $user->user_id }}
+ {{ $user->user_id }}
@php
$showWebCount = auth()->user()?->is_super_admin && $user->web_permission_count > 0;
$showApiCount = $user->api_permission_count > 0;