Permission 모듈
이 문서는 검토 중입니다. 내용이 변경될 수 있습니다.
권한 시스템 (Level 0~6, ADR-058) Core 모듈의 상세 사용법입니다.
개요
Permission 모듈은 Multi-SaaS Kit의 **권한 시스템 (Level 0~6, ADR-058)**을 구현합니다. 숫자가 낮을수록 높은 권한을 가지며, 상위 권한은 하위 권한을 포함합니다.
packages/core/Base/Permission/
├── Contracts/
│ └── PermissionServiceInterface.php
├── Enums/
│ └── UserLevel.php # 권한 분류 (Level 0~6) Enum
├── Middleware/
│ └── EnsureUserLevel.php # 라우트 권한 체크
├── Policies/
│ ├── UserPolicy.php # User CRUD 권한
│ └── TenantOwnedPolicy.php # 테넌트 소유 모델
├── Services/
│ └── PermissionService.php # 권한 검증 서비스
└── Traits/
└── HasLevel.php # User 모델 Trait
UserLevel Enum
권한 레벨을 정의하는 Enum입니다.
namespace App\Core\Base\Permission\Enums;
enum UserLevel: int
{
case PLATFORM_ADMIN = 0; // 플랫폼 운영자
case SAAS_ADMIN = 1; // SaaS 서비스 관리자
case TENANT_ADMIN = 2; // 테넌트(고객사) 관리자
case ORGANIZATION_ADMIN = 3; // 조직/부서 관리자
case WORKSPACE_ADMIN = 4; // 프로젝트/작업공간 관리자
case GROUP_LEADER = 5; // 팀/그룹 리더
case MEMBER = 6; // 일반 멤버
}
Enum 메서드
use App\Core\Base\Permission\Enums\UserLevel;
// 레벨 값 가져오기
UserLevel::TENANT_ADMIN->value; // 2
// 레벨 라벨 가져오기
UserLevel::TENANT_ADMIN->label(); // "Tenant Admin"
// 값으로 Enum 생성
UserLevel::tryFromValue(2); // UserLevel::TENANT_ADMIN
// 플랫폼 레벨 여부 (0-1)
UserLevel::TENANT_ADMIN->isPlatformLevel(); // false
// 테넌트 레벨 여부 (2-6)
UserLevel::TENANT_ADMIN->isTenantLevel(); // true
// 테넌트 격리 우회 가능 여부
UserLevel::SAAS_ADMIN->canBypassTenantIsolation(); // true
HasLevel Trait
User 모델에 적용하여 권한 분류 (Level 0~6) 기능을 추가합니다.
적용 방법
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Core\Base\Permission\Traits\HasLevel;
use App\Core\Contracts\UserInterface;
class User extends Authenticatable implements UserInterface
{
use HasLevel;
protected $fillable = [
'name', 'email', 'password', 'level', 'tenant_id',
];
protected $casts = [
'level' => 'integer',
];
}
권한 확인 메서드
$user = auth()->user();
// 현재 레벨 가져오기
$user->getLevel(); // int: 2
$user->getUserLevel(); // UserLevel::TENANT_ADMIN
$user->getLevelLabel(); // "Tenant Admin"
// 최소 레벨 확인 (숫자가 작거나 같으면 true)
$user->hasLevelAtLeast(UserLevel::TENANT_ADMIN); // true
$user->hasLevelAtLeast(UserLevel::SAAS_ADMIN); // false
// 정확한 레벨 확인
$user->hasLevelExact(UserLevel::TENANT_ADMIN); // true
// 범위 확인
$user->hasLevelBetween(
UserLevel::TENANT_ADMIN, // min
UserLevel::GROUP_LEADER // max
); // true (Level 2-5 사이)
역할 확인 메서드
$user->isPlatformAdmin(); // Level 0인지
$user->isSaasAdmin(); // Level 0-1인지
$user->isTenantAdmin(); // Level 0-2인지
$user->isOrgAdmin(); // Level 0-3인지
$user->isWorkspaceAdmin(); // Level 0-4인지
$user->isGroupLeader(); // Level 0-5인지
사용자 관리 권한 확인
// 대상 사용자를 관리할 수 있는지
$admin = User::where('level', 2)->first(); // Tenant Admin
$member = User::where('level', 6)->first(); // Member
$admin->canManageUser($member); // true (하위 레벨)
$member->canManageUser($admin); // false (상위 레벨)
테넌트 격리 우회
// 테넌트 격리 우회 가능한지 (Level 0-1)
$user->canBypassTenantIsolation();
쿼리 스코프
// 특정 레벨 사용자만 조회
User::withLevel(UserLevel::MEMBER)->get();
// 특정 레벨 이상 (더 높은 권한)
User::withLevelOrAbove(UserLevel::TENANT_ADMIN)->get(); // Level 0-2
// 특정 레벨 이하 (더 낮은 권한)
User::withLevelOrBelow(UserLevel::GROUP_LEADER)->get(); // Level 5-6
PermissionService
권한 검증 로직을 캡슐화한 서비스입니다.
의존성 주입
use App\Core\Base\Permission\Contracts\PermissionServiceInterface;
class UserController extends Controller
{
public function __construct(
private PermissionServiceInterface $permissionService
) {}
}
주요 메서드
// 레벨 접근 가능 여부
$this->permissionService->canAccessLevel($user, 3);
// 패널 접근 가능 여부
$this->permissionService->canAccessPanel($user, 'tenant');
// 사용자 관리 가능 여부
$this->permissionService->canManageUser($actor, $target);
// 특정 레벨 사용자 생성 가능 여부
$this->permissionService->canCreateUserWithLevel($actor, 4);
// 할당 가능한 레벨 목록
$this->permissionService->getAssignableLevels($actor);
// [3 => 'Organization Admin', 4 => 'Workspace Admin', ...]
// 모든 레벨 목록
$this->permissionService->getAllLevels();
// 레벨 계층 구조
$this->permissionService->getLevelHierarchy();
UserPolicy
User 모델의 CRUD 권한을 정의합니다.
핵심 원칙
| 원칙 | 설명 |
|---|---|
| Downward Access | 상위 레벨만 하위 레벨 관리 가능 |
| Tenant Isolation | Level 2+는 같은 테넌트만 접근 |
| Self Protection | 자기 자신 수정/삭제 불가 |
Policy 메서드
namespace App\Core\Base\Permission\Policies;
class UserPolicy
{
// 목록 조회 - 항상 허용 (패널 미들웨어에서 제어)
public function viewAny(UserInterface $user): bool
{
return true;
}
// 개별 조회
public function view(UserInterface $user, UserInterface $model): bool
{
// 자기 자신 항상 조회 가능
// 상위 레벨만 하위/동일 레벨 조회
// Level 2+는 같은 테넌트만
}
// 생성
public function create(UserInterface $user, int $targetLevel): bool
{
// 자신보다 하위 레벨만 생성 가능
return $user->getLevel() < $targetLevel;
}
// 수정
public function update(UserInterface $user, UserInterface $model): bool
{
// 자기 자신 수정 불가 (프로필 사용)
// 자신보다 하위 레벨만 수정 가능
// Level 2+는 같은 테넌트만
}
// 삭제
public function delete(UserInterface $user, UserInterface $model): bool
{
// update와 동일
}
// Bulk Delete - Level 0-1만
public function deleteAny(UserInterface $user): bool
{
return $user->getLevel() <= UserLevel::SAAS_ADMIN->value;
}
// Force Delete - Level 0만
public function forceDelete(UserInterface $user, UserInterface $model): bool
{
return $user->getLevel() === UserLevel::PLATFORM_ADMIN->value
&& $user->getLevel() < $model->getLevel();
}
}
Controller에서 사용
class UserController extends Controller
{
public function update(User $user, UpdateUserRequest $request)
{
$this->authorize('update', $user);
// 수정 로직...
}
public function destroy(User $user)
{
$this->authorize('delete', $user);
// 삭제 로직...
}
}
TenantOwnedPolicy
테넌트 소속 모델(Post, Order 등)의 CRUD 권한을 정의합니다.
등록 방법
// config/core.php
'authorization' => [
'tenant_owned_models' => [
\App\Models\Post::class,
\App\Models\Order::class,
],
],
동작 원리
class TenantOwnedPolicy
{
// 목록 조회 - 항상 허용 (TenantScope가 필터링)
public function viewAny(): bool
{
return true;
}
// 개별 조회 - 같은 테넌트만 또는 플랫폼 관리자
public function view(UserInterface $user, $model): bool
{
if ($user->canBypassTenantIsolation()) {
return true;
}
return $user->getTenantId() === $model->tenant_id;
}
// 생성 - 인증된 사용자
public function create(UserInterface $user): bool
{
return true;
}
// 수정/삭제 - 같은 테넌트만
public function update(UserInterface $user, $model): bool
{
if ($user->canBypassTenantIsolation()) {
return true;
}
return $user->getTenantId() === $model->tenant_id;
}
}
EnsureUserLevel Middleware
라우트에서 특정 레벨만 접근을 허용합니다.
등록 (자동)
// CoreServiceProvider에서 자동 등록
$router->aliasMiddleware('level', EnsureUserLevel::class);
사용법
// 단일 레벨
Route::middleware(['auth', 'level:0'])
->prefix('platform')
->group(function () {
// Platform Admin 전용
});
// 여러 레벨
Route::middleware(['auth', 'level:0,1,2'])
->prefix('admin')
->group(function () {
// Level 0, 1, 2 접근 가능
});
Filament 패널 적용
// app/Providers/Filament/TenantPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel
->id('tenant')
->path(env('PANEL_PATH_TENANT', 'tenant'))
->authMiddleware([
Authenticate::class,
EnsureUserLevel::class.':0,1,2',
]);
}
Gate 정의
Gate로 전역 권한 규칙을 정의합니다.
예시
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Gate;
use App\Core\Base\Permission\Enums\UserLevel;
public function boot(): void
{
// 관리자 접근
Gate::define('access-admin', function ($user) {
return $user->hasLevelAtLeast(UserLevel::TENANT_ADMIN);
});
// 시스템 설정
Gate::define('manage-system', function ($user) {
return $user->isPlatformAdmin();
});
// 사용자 관리
Gate::define('manage-users', function ($user, $target = null) {
if ($target) {
return $user->canManageUser($target);
}
return $user->hasLevelAtLeast(UserLevel::GROUP_LEADER);
});
}
사용
// Controller
if (Gate::allows('manage-system')) {
// 시스템 설정 가능
}
// Blade
@can('access-admin')
<a href="/admin">관리자</a>
@endcan
Filament Resource 통합
canViewAny / canCreate
namespace App\Filament\Tenant\Resources;
use Filament\Resources\Resource;
use App\Core\Base\Permission\Enums\UserLevel;
class OrganizationResource extends Resource
{
public static function canViewAny(): bool
{
return auth()->user()->hasLevelAtLeast(UserLevel::TENANT_ADMIN);
}
public static function canCreate(): bool
{
return auth()->user()->hasLevelAtLeast(UserLevel::TENANT_ADMIN);
}
}
canEdit / canDelete
public static function canEdit($record): bool
{
$user = auth()->user();
// Platform/SaaS Admin은 모두 수정 가능
if ($user->isSaasAdmin()) {
return true;
}
// Tenant Admin은 자신의 테넌트만
if ($user->isTenantAdmin()) {
return $user->tenant_id === $record->tenant_id;
}
return false;
}
public static function canDelete($record): bool
{
return static::canEdit($record)
&& auth()->user()->hasLevelAtLeast(UserLevel::TENANT_ADMIN);
}
레벨 선택 필드
use App\Core\Base\Permission\Enums\UserLevel;
use App\Core\Base\Permission\Contracts\PermissionServiceInterface;
public static function form(Form $form): Form
{
$permissionService = app(PermissionServiceInterface::class);
$assignableLevels = $permissionService->getAssignableLevels(auth()->user());
return $form->schema([
Select::make('level')
->label('권한 레벨')
->options($assignableLevels)
->required(),
]);
}
테스트 작성
Unit 테스트
namespace Tests\Unit\Core\Permission;
use Tests\TestCase;
use App\Models\User;
use App\Core\Base\Permission\Enums\UserLevel;
class HasLevelTest extends TestCase
{
public function test_has_level_at_least(): void
{
$admin = User::factory()->create(['level' => 2]);
$this->assertTrue($admin->hasLevelAtLeast(UserLevel::TENANT_ADMIN));
$this->assertTrue($admin->hasLevelAtLeast(UserLevel::MEMBER));
$this->assertFalse($admin->hasLevelAtLeast(UserLevel::SAAS_ADMIN));
}
public function test_can_manage_user(): void
{
$admin = User::factory()->create([
'level' => 2,
'tenant_id' => 1,
]);
$member = User::factory()->create([
'level' => 6,
'tenant_id' => 1,
]);
$otherTenantMember = User::factory()->create([
'level' => 6,
'tenant_id' => 2,
]);
$this->assertTrue($admin->canManageUser($member));
$this->assertFalse($admin->canManageUser($otherTenantMember));
}
}
Feature 테스트
namespace Tests\Feature\Core\Permission;
use Tests\TestCase;
use App\Models\User;
class UserPolicyTest extends TestCase
{
public function test_tenant_admin_cannot_edit_other_tenant_user(): void
{
$admin = User::factory()->create([
'level' => 2,
'tenant_id' => 1,
]);
$otherUser = User::factory()->create([
'level' => 6,
'tenant_id' => 2,
]);
$response = $this->actingAs($admin)
->putJson("/api/users/{$otherUser->id}", ['name' => 'Hacked']);
$response->assertForbidden();
}
public function test_platform_admin_can_edit_any_user(): void
{
$platformAdmin = User::factory()->create(['level' => 0]);
$anyUser = User::factory()->create(['level' => 6, 'tenant_id' => 99]);
$response = $this->actingAs($platformAdmin)
->putJson("/api/users/{$anyUser->id}", ['name' => 'Updated']);
$response->assertOk();
}
}
모범 사례
최소 권한 원칙
// ✅ 좋은 예: 필요한 최소 레벨 확인
if ($user->hasLevelAtLeast(UserLevel::TENANT_ADMIN)) {
// 테넌트 관리 기능
}
// ❌ 나쁜 예: 불필요하게 높은 권한 요구
if ($user->isPlatformAdmin()) {
// 모든 기능에 Platform Admin 요구
}
테넌트 컨텍스트 확인
// ✅ 좋은 예: 테넌트 소속 확인
if ($user->isTenantAdmin() && $user->tenant_id === $resource->tenant_id) {
// 자신의 테넌트 리소스만
}
// ❌ 나쁜 예: 테넌트 확인 누락
if ($user->isTenantAdmin()) {
// 다른 테넌트 데이터 접근 가능
}
Policy 사용 권장
// ✅ 좋은 예: Policy 사용
$this->authorize('update', $organization);
// ❌ 나쁜 예: 직접 권한 로직 작성
if ($user->level <= 2 && $user->tenant_id === $org->tenant_id) {
// 로직 중복
}
서브관리자 메뉴별 권한 강제 (Feature Permission)
_template v1.38.0(Phase A — 강제 배선 완성) + v1.40.0(플러그인 리소스로 확장)에서 도입되었습니다. Core 가 symlink 로 전 프로젝트에 공유되므로, 코드는 즉시 공유되지만 동작 활성화에는 각 프로젝트의 마이그레이션(아래)이 필요합니다.
앞 절의 Level 0~6 시스템이 "어느 레벨까지 접근하는가"(계층 + 테넌트 격리)를 다룬다면, Feature Permission 은 **"같은 레벨 안에서 어떤 메뉴(기능)를 CRUD 할 수 있는가"**를 다룹니다. 두 시스템은 독립적이며, 메뉴 접근은 레벨 통제 AND Feature 권한으로 함께 적용됩니다.
개념 — Chief Admin vs Sub Admin
독립 사이트(SaaS/Tenant 등)를 운영하다 보면, 한 관리자(예: SaaS Admin)가 직원에게 메뉴별로 제한된 권한만 주고 싶을 때가 있습니다. 예를 들어 "이 직원은 게시판만 관리하고, 사용자 관리 메뉴는 보지 못하게" 하는 식입니다. 이를 위해 동일 레벨 안에서 관리자를 두 종류로 구분합니다.
| 구분 | 식별 | 권한 |
|---|---|---|
| Chief Admin | is_chief = true | 해당 레벨의 모든 메뉴 전권 (제한 없음) |
| 1차(기본) 관리자 | is_chief = false, chief_id = null | 전권 (AD9 — 아래 참조) |
| Sub Admin | is_chief = false, chief_id 보유 | 명시 부여된 메뉴/액션만 사용 가능 |
is_chief: 해당 관리자가 사이트의 주 관리자(Chief)인지 여부.chief_id: 이 Sub Admin 을 만든 상위 Chief 의 사용자 ID. 권한 부여 UI(폼)를 통해 Chief 아래에 생성된 서브관리자만 이 값을 가집니다.
user_permissions 모델 (App\Models\UserPermission)
Sub Admin 의 메뉴별 권한은 user_permissions 테이블에 기능(feature) × 액션 단위로 저장됩니다.
| 컬럼 | 타입 | 의미 |
|---|---|---|
user_id | int | 권한 소유자(Sub Admin) |
feature | string | 기능 식별자 (config/permissions.php 키, 예: boards, users) |
can_create / can_read / can_update / can_delete | boolean | 기본 CRUD 권한 |
custom_actions | JSON (array cast) | 특수 액션 권한 ({"manage_notice": true, ...}) |
관련 마이그레이션:
2025_12_24_000004_create_user_permissions_table.php2026_04_02_000001_add_custom_actions_to_user_permissions_table.php
Zero-default(기본 차단) 원칙: UserPermission::findOrCreateFor() 는 신규 레코드를 모든 CRUD false 로 생성합니다. 즉 Sub Admin 은 명시적으로 부여받기 전까지 아무 메뉴도 보거나 사용할 수 없습니다. 권한이 없으면 안전하게 차단되는 fail-safe 설계입니다.
// 액션 판정 (App\Models\UserPermission::can)
// create / read(view,list,index) / update(edit) / delete(destroy) → CRUD 컬럼
// 그 외 → custom_actions[$action] === true 여부
$permission->can('read'); // can_read
$permission->can('manage_notice'); // custom_actions['manage_notice'] === true
AD9 — 1차(기본) 관리자 전권 (시드 관리자 무잠금 안전 기반)
Feature Permission 강제를 도입할 때 가장 위험한 시나리오는, 시드로 만들어진 기존 1차 관리자(예: tenant@, org@)가 갑자기 모든 메뉴에서 잠기는 것입니다. 이들은 is_chief = false 이면서 권한 부여 폼을 거치지 않았으므로 chief_id 도 없습니다.
AD9 규칙: chief_id === null 인 관리자는 1차(기본) 관리자로 간주하여 전권을 부여합니다.
- 시드/기존 1차 관리자(
chief_id = null) → 잠기지 않음. 데이터 변경 0. - 권한 부여 폼으로 Chief 아래 생성된 Sub Admin(
chief_id보유) → 명시 권한만 적용.
이로써 "강제를 켜는 순간 운영 중인 관리자가 잠기는" 회귀 없이, 새로 만든 서브관리자에게 만 제한이 걸립니다.
강제 적용 레벨 — sub_admin_levels
어떤 레벨에서 Feature Permission 을 강제할지는 config/core.php 에서 제어합니다.
// config/core.php
'permission' => [
'sub_admin_levels' => [1, 2, 3, 4, 5], // L1~L5 서브관리자 강제
// ...
],
'models' => [
'user_permission' => \App\Models\UserPermission::class,
],
- Platform Admin(L0)은 항상 전권이므로 대상 외.
- Member(L6)는 최하위라 대상 외.
- v1.38.0 이전에는 L1 만 평가했으나, 본 릴리스에서 L1~L5 전체로 확대되었습니다.
hasPermission 판정 순서
권한 런타임은 Core 트레이트 App\Core\Base\Permission\Traits\HasFeaturePermissions 의 hasPermission(string $feature, string $action) 으로 일원화됩니다. App\Models\User 가 이 트레이트를 채택합니다(HasLevel 과 함께). 판정 순서는 다음과 같습니다.
| 순서 | 조건 | 결과 |
|---|---|---|
| 1 | level === 0 (Platform Admin) | true (전권) |
| 2 | level 이 sub_admin_levels 에 없음 | true (기존 계층 권한만 사용) |
| 3 | is_chief === true (Chief Admin) | true (전권) |
| 4 | chief_id === null (1차 관리자, AD9) | true (전권) |
| 5 | 그 외 (Sub Admin) | 해당 feature 의 UserPermission 레코드를 조회해 $permission?->can($action) ?? false |
5번에서 레코드 자체가 없으면 false 입니다. 즉 부여받지 않은 메뉴는 자동 차단됩니다(zero-default).
// 사용 예
if (auth()->user()->hasPermission('boards', 'read')) {
// 게시판 메뉴 노출/조회 허용
}
if (auth()->user()->hasPermission('boards', 'manage_notice')) {
// 공지 관리 액션 노출
}
Filament 강제 — HasFeaturePermissionCheck
부여(grant) 모델만으로는 강제가 되지 않습니다. v1.38.0 이전에는 부여 UI/모델은 완비됐으나 강제가 어느 Resource 에도 배선되지 않아 사실상 미작동이었습니다. 본 릴리스에서 Filament Resource 에 강제 트레이트를 배선해 루프를 닫았습니다.
Resource 에 App\Core\Base\Permission\Filament\Traits\HasFeaturePermissionCheck 를 채택하고 getFeatureKey() 만 구현하면, 다음 메서드가 자동으로 hasPermission 을 호출합니다.
| 메서드 | 매핑 액션 | 효과 |
|---|---|---|
shouldRegisterNavigation() | read | 권한 없으면 네비게이션 메뉴 숨김 |
canAccess() | read | 리소스 접근 차단 |
canViewAny() | read | 목록 페이지 — 직접 URL 접근 차단 |
canView($record) | read | 단일 레코드 조회 차단 |
canCreate() | create | 등록 차단 |
canEdit($record) | update | 수정 차단 |
canDelete($record) | delete | 삭제 차단 |
canViewAny() / canView() 는 v1.38.0 에서 보강된 항목으로, 네비게이션을 숨겨도 URL 을 직접 입력하는 우회를 막습니다.
namespace App\Filament\Saas\Resources;
use App\Core\Base\Permission\Filament\Traits\HasFeaturePermissionCheck;
use Filament\Resources\Resource;
class BoardResource extends Resource
{
use HasFeaturePermissionCheck;
protected static function getFeatureKey(): string
{
return 'boards'; // config/permissions.php 키
}
}
내장 리소스 배선(v1.38.0): 내장 게시판 BoardResource(boards) / InquiryResource(inquiries) + Core BaseAllUsersResource(users, 5개 패널 공유). 기존 토글(예: board.enabled · 독립사이트 게이트)이 있는 리소스는 AND 병합되어, 토글이 켜져 있어도 read 권한 없는 Sub Admin 은 메뉴를 볼 수 없습니다.
플러그인 리소스 강제 (v1.40.0)
v1.40.0 은 강제를 플러그인 리소스(외부 Board / Commerce)로 확장했습니다. 플러그인 리소스는 운영자가 plugin 을 on/off 하는 PluginActivation 매트릭스를 이미 가지고 있으므로, 두 조건을 AND 병합합니다.
| 플러그인 | 리소스 | feature 키 |
|---|---|---|
| 외부 Board | Saas/Tenant × Board/Post Resource | boards |
| Commerce | Saas/Tenant × Product/Coupon Resource | products |
| Commerce | Saas/Tenant × Order Resource | orders |
// 플러그인 리소스의 네비게이션 = 매트릭스 활성 AND feature read
public static function shouldRegisterNavigation(): bool
{
return static::isEnabledForCurrentScope() // PluginActivation 매트릭스
&& auth()->user()->hasPermission(static::getFeatureKey(), 'read');
}
트레이트가 canViewAny/canCreate/canEdit/canDelete 를 자동 강제하므로, 매트릭스로 켜진 메뉴라도 Sub Admin 은 부여받은 CRUD 만 수행합니다. 매트릭스 토글 동작은 보존됩니다(매트릭스 테스트의 SaaS Admin 은 chief_id = null 1차 관리자 → AD9 로 전권 → 기존 토글 동작 그대로).
운영자 사용법 — 서브관리자에게 메뉴별 권한 부여
- Chief Admin 또는 1차 관리자로 로그인합니다.
- 사용자(서브관리자) 생성/편집 화면에서 권한 설정 폼을 엽니다. 이 폼은
config/permissions.php의 feature 목록(라벨/CRUD/특수 권한)을 자동으로 표시합니다. - 메뉴(feature)별로 필요한 권한만 체크합니다.
- 기본 권한: 조회(read) / 등록(create) / 수정(update) / 삭제(delete)
- 특수 권한: 메뉴별 고유 액션(아래 예시)
- 저장하면 해당 서브관리자는 체크한 메뉴만 네비게이션에 보이고, 체크한 액션만 수행할 수 있습니다. 체크하지 않은 메뉴는 메뉴/직접 URL 모두 차단됩니다.
폼을 통해 생성된 서브관리자는
chief_id를 갖게 되어 강제 대상이 됩니다. 기존 1차 관리자(chief_id = null)는 AD9 로 전권을 유지하므로 잠기지 않습니다.
custom_actions 예시 — boards
메뉴 기본 CRUD 외에, 게시판처럼 고유 액션이 필요한 기능은 config/permissions.php 의 custom_actions 로 정의합니다.
// config/permissions.php
'boards' => [
'label' => '게시판 관리',
'description' => '게시판 글 등록·수정·삭제 + 공지/FAQ 관리 + 답글',
'crud' => true,
'custom_actions' => [
'manage_notice' => '공지 관리',
'manage_faq' => 'FAQ 관리',
'reply' => '답글 작성',
],
],
부여 예시(공지/FAQ 담당자 — 게시 판 조회 + 공지/FAQ 관리만, 글 작성/삭제 불가):
$subAdmin->setPermissions([
'boards' => [
'read' => true,
'create' => false,
'update' => true,
'delete' => false,
'manage_notice' => true,
'manage_faq' => true,
'reply' => false,
],
]);
setPermissions() 는 create/read/update/delete 를 CRUD 컬럼으로, 그 외 키(manage_notice 등)는 custom_actions JSON 으로 자동 분리해 저장합니다. 액션 화면에서는 auth()->user()->hasPermission('boards', 'manage_notice') 로 노출 여부를 게이트합니다.
정리
| 항목 | 내용 |
|---|---|
| 강제 대상 | sub_admin_levels = [1,2,3,4,5] 의 Sub Admin (is_chief=false 이고 chief_id 보유) |
| 무제한(전권) | Platform Admin(L0), Chief Admin, 1차 관리자(chief_id=null, AD9) |
| 기본값 | zero-default — 부여 전까지 모든 메뉴 차단 |
| 권한 런타임 | HasFeaturePermissions::hasPermission($feature, $action) |
| Filament 강제 | HasFeaturePermissionCheck (네비 숨김 + 직접 URL 차단 + CRUD) |
| 정의 위치 | config/permissions.php (feature/CRUD/custom_actions) |
| 플러그인 | v1.40.0 — PluginActivation 매트릭스 AND feature read 병합 |
관련 문서
- 권한 시스템 개념 - 권한 분류 (Level 0~6) 개념
- Tenant 모듈 - 테넌트 격리
- Core 모듈 개요 - 전체 구조