authorizeAdminAccess(); * * Example: * public function deletePost($id) * { * $this->authorizeAdminAccess(); // CRITICAL: Check on every call * * // Safe to proceed with deletion * Post::destroy($id); * } */ trait RequiresAdminAuthorization { /** * Cached authorization result to avoid repeated checks within same request * * @var bool|null */ private $adminAuthorizationChecked = null; /** * Authorize admin access for the current operation. * * This method MUST be called at the start of every sensitive operation * (create, update, delete, etc.) to prevent unauthorized access. * * Validates: * - Active profile exists in session * - Profile ownership via ProfileAuthorizationHelper (prevents cross-guard attacks) * - Admin or central bank (level=0) permissions * * @param bool $forceRecheck Force a new authorization check even if already checked * @return void * @throws \Symfony\Component\HttpKernel\Exception\HttpException (403 or 401) */ protected function authorizeAdminAccess(bool $forceRecheck = false): void { // Use cached result unless force recheck if (!$forceRecheck && $this->adminAuthorizationChecked === true) { return; } // Get active profile from session $activeProfileType = session('activeProfileType'); $activeProfileId = session('activeProfileId'); if (!$activeProfileType || !$activeProfileId) { Log::warning('RequiresAdminAuthorization: No active profile in session', [ 'component' => static::class, 'method' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'] ?? 'unknown', 'ip_address' => request()->ip(), ]); abort(403, __('No active profile selected')); } $profile = $activeProfileType::find($activeProfileId); if (!$profile) { Log::warning('RequiresAdminAuthorization: Active profile not found', [ 'component' => static::class, 'active_profile_type' => $activeProfileType, 'active_profile_id' => $activeProfileId, 'method' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'] ?? 'unknown', 'ip_address' => request()->ip(), ]); abort(403, __('Active profile not found')); } // Validate profile ownership using ProfileAuthorizationHelper // This prevents cross-guard attacks (e.g., web user accessing admin profile) ProfileAuthorizationHelper::authorize($profile); // Verify admin or central bank permissions if ($profile instanceof \App\Models\Admin) { // Admin access OK $this->adminAuthorizationChecked = true; return; } if ($profile instanceof \App\Models\Bank) { // Only central bank (level 0) can access admin functions if ($profile->level === 0) { $this->adminAuthorizationChecked = true; return; } Log::warning('RequiresAdminAuthorization: Non-central bank attempted admin operation', [ 'component' => static::class, 'method' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'] ?? 'unknown', 'bank_id' => $profile->id, 'bank_level' => $profile->level, 'ip_address' => request()->ip(), ]); abort(403, __('Central bank access required for admin operations')); } // Not admin or central bank Log::warning('RequiresAdminAuthorization: Unauthorized profile type attempted admin operation', [ 'component' => static::class, 'method' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'] ?? 'unknown', 'profile_type' => get_class($profile), 'profile_id' => $profile->id, 'ip_address' => request()->ip(), ]); abort(403, __('Admin or central bank access required')); } /** * Reset authorization cache. * * Call this if you need to force a fresh authorization check * (e.g., after switching profiles within the same component lifecycle). * * @return void */ protected function resetAdminAuthorization(): void { $this->adminAuthorizationChecked = null; } /** * Check if admin access is authorized without throwing an exception. * * Useful for conditional UI rendering or feature availability checks. * * @return bool True if authorized, false otherwise */ protected function canPerformAdminActions(): bool { try { $this->authorizeAdminAccess(); return true; } catch (\Throwable $e) { return false; } } }