본문으로 건너뛰기

권한 관련 문제

권한 시스템 (Level 0~6, ADR-058) 사용 시 발생할 수 있는 문제와 해결 방법입니다.

403 Forbidden 오류

권한 레벨 부족

증상:

{
"success": false,
"message": "권한이 부족합니다.",
"error": {
"code": "forbidden",
"required_level": 2,
"current_level": 6
}
}

원인:

  • 현재 사용자 권한이 요청된 작업에 부족

해결 방법:

  1. 현재 권한 확인

    // Tinker에서
    auth()->user()->permission_level;
    // 결과: 6 (Member)
  2. 필요 권한 확인

    // 해당 리소스의 Policy 확인
    // app/Policies/UserPolicy.php
    public function update(User $user, User $model)
    {
    return $user->permission_level <= $model->permission_level;
    }
  3. 권한 상승 요청

    • 상위 관리자에게 권한 변경 요청

범위(Scope) 제한

증상:

{
"success": false,
"message": "접근할 수 없는 리소스입니다."
}

원인:

  • 다른 테넌트/조직/팀의 리소스 접근 시도

확인 방법:

// 현재 사용자의 접근 범위
$user = auth()->user();
echo "Tenant: {$user->tenant_id}";
echo "Organization: {$user->organization_id}";
echo "Team: {$user->team_id}";

// 요청된 리소스의 소속
$resource = User::find($targetId);
echo "Target Tenant: {$resource->tenant_id}";

권한 레벨별 범위:

Level접근 가능 범위
0-1모든 테넌트
2자기 테넌트 내 모든 데이터
3자기 조직 내 모든 데이터
4자기 워크스페이스 내 데이터
5자기 팀 내 데이터
6자기 데이터만

권한이 적용되지 않음

Policy 미등록

증상:

  • 권한 검사가 작동하지 않음
  • 모든 사용자가 접근 가능

확인 방법:

// app/Providers/AuthServiceProvider.php
protected $policies = [
User::class => UserPolicy::class, // 등록 확인
];

해결 방법:

// AuthServiceProvider.php에 추가
protected $policies = [
\App\Models\User::class => \App\Policies\UserPolicy::class,
\App\Models\Organization::class => \App\Policies\OrganizationPolicy::class,
];

Gate/Policy 미적용

증상:

  • Controller에서 권한 검사 누락

확인 방법:

// Controller 메서드 확인
public function update(Request $request, User $user)
{
// ❌ 권한 검사 없음
$user->update($request->all());
}

해결 방법:

public function update(Request $request, User $user)
{
// ✅ authorize 추가
$this->authorize('update', $user);

$user->update($request->validated());
}

미들웨어 누락

증상:

  • 인증 없이 접근 가능

확인 방법:

// routes/api.php
Route::get('/users', [UserController::class, 'index']); // ❌ 미들웨어 없음

해결 방법:

Route::middleware(['auth:sanctum', 'tenant'])->group(function () {
Route::get('/users', [UserController::class, 'index']); // ✅
});

권한 상속 문제

상위 권한이 적용 안 됨

증상:

  • Tenant Admin(Level 2)인데 Organization Admin(Level 3) 작업 불가

원인:

  • 권한 비교 로직 오류

확인 방법:

// 올바른 비교 (숫자가 작을수록 상위)
if ($user->permission_level <= $requiredLevel) {
// 권한 있음
}

// ❌ 잘못된 비교
if ($user->permission_level >= $requiredLevel) {
// 로직 오류
}

레벨 구조:

Level 0 (가장 높음) → Platform Admin
Level 1 → SaaS Admin
Level 2 → Tenant Admin
Level 3 → Organization Admin
Level 4 → Workspace Admin
Level 5 → Team Leader
Level 6 (가장 낮음) → Member

자신보다 높은 권한 부여 시도

증상:

{
"success": false,
"message": "자신보다 높은 권한을 부여할 수 없습니다."
}

원인:

  • 권한 에스컬레이션 방지 로직

확인:

// 권한 변경 검증
if ($newLevel < auth()->user()->permission_level) {
throw new AuthorizationException(
'자신보다 높은 권한을 부여할 수 없습니다.'
);
}

해결 방법:

  • 상위 관리자에게 권한 변경 요청
  • 또는 Platform Admin(Level 0)이 처리

캐시 관련 문제

권한 변경 후 미반영

증상:

  • 권한 변경 후에도 이전 권한으로 동작

원인:

  • 권한 정보 캐싱

해결 방법:

  1. 캐시 정리

    php artisan cache:clear
  2. 사용자 재로그인

    • 세션/토큰 갱신 필요
  3. 캐시 키 확인

    // 권한 캐시 삭제
    Cache::forget("user:{$userId}:permissions");

토큰 재발급 필요

증상:

  • API 토큰에 이전 권한 정보 포함

해결 방법:

// 기존 토큰 삭제 후 재발급
$user->tokens()->delete();
$newToken = $user->createToken('api-token')->plainTextToken;

테넌트 격리 문제

다른 테넌트 데이터 접근

증상:

  • 다른 테넌트의 데이터가 조회됨 (보안 문제!)

원인:

  • TenantScope 미적용
  • 직접 쿼리로 우회

확인 방법:

// Model에 Scope 확인
class User extends Model
{
use BelongsToTenant; // 이 trait 필수
}

해결 방법:

// ❌ 직접 쿼리 (Scope 우회)
DB::table('users')->where('id', 1)->first();

// ✅ Eloquent 사용 (Scope 자동 적용)
User::find(1);

테넌트 컨텍스트 미설정

증상:

  • 테넌트 관련 쿼리에서 빈 결과

확인 방법:

// 현재 테넌트 확인
dd(tenant()); // null이면 미설정

해결 방법:

// 미들웨어 확인
Route::middleware(['auth:sanctum', 'tenant'])->group(...);

// 또는 수동 설정
tenancy()->initialize(Tenant::find(1));

디버깅 방법

권한 로그 활성화

// config/logging.php channels에 추가
'permission' => [
'driver' => 'daily',
'path' => storage_path('logs/permission.log'),
'level' => 'debug',
],

// 사용
Log::channel('permission')->debug('Permission check', [
'user_id' => auth()->id(),
'action' => 'update',
'resource' => User::class,
'resource_id' => $id,
]);

Tinker로 권한 테스트

php artisan tinker
$user = User::find(1);
$targetUser = User::find(2);

// 권한 검사
Gate::forUser($user)->allows('update', $targetUser); // true/false

// 권한 레벨 확인
$user->permission_level; // 0-6

// 수행 가능 작업
$user->can('update', $targetUser);
$user->cannot('delete', $targetUser);

요청 로그 확인

// Middleware에서 로깅
public function handle($request, Closure $next)
{
Log::debug('Permission Middleware', [
'user' => auth()->id(),
'level' => auth()->user()?->permission_level,
'path' => $request->path(),
'method' => $request->method(),
]);

return $next($request);
}

일반적인 실수

1. Gate와 Policy 혼용 오류

// ❌ 잘못된 사용
Gate::allows('update', [$user, $post]); // Policy 형식으로 호출

// ✅ 올바른 사용
$user->can('update', $post); // Policy
Gate::allows('admin-only'); // Gate

2. 권한 비교 방향 오류

// ❌ 잘못됨 (Level 0이 가장 높음을 잊음)
if ($user->permission_level >= 2) {
// Tenant Admin 이상...
}

// ✅ 올바름
if ($user->permission_level <= 2) {
// Tenant Admin(2) 이상 (0, 1, 2)
}

3. 자기 참조 수정

// ❌ 자기 권한 변경 허용
$user->update(['permission_level' => 0]);

// ✅ 자기 권한 변경 방지
if ($user->id === auth()->id()) {
throw new AuthorizationException('본인 권한은 변경할 수 없습니다.');
}

권한 초기화 (긴급 복구)

주의

운영 환경에서는 신중하게 사용하세요.

# Platform Admin 계정 생성/복구
php artisan tinker
$admin = User::updateOrCreate(
['email' => 'admin@example.com'],
[
'name' => 'Platform Admin',
'password' => bcrypt('secure-password'),
'permission_level' => 0,
]
);

관련 문서