Fifth audit revealed additional permission system components: **Controllers:** - app/Http/Controllers/ViewAsController.php → Start/end user impersonation sessions - app/Http/Controllers/ViewSwitcherController.php → Module view switching functionality - app/Http/Controllers/Business/UserPermissionsController.php → Update user permissions, apply role templates **Views:** - resources/views/business/users/permissions-modal.blade.php → Permission editing UI modal - resources/views/components/view-as-banner.blade.php → Shows active impersonation banner - resources/views/components/view-switcher.blade.php → Module view switcher component **Traits:** - app/Traits/HasHashid.php → Hashid generation for models (used by Product, Brand, etc.) These complete the permission and impersonation system that analytics controllers depend on for access control. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
181 lines
5.9 KiB
PHP
181 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\User;
|
|
use App\Models\ViewAsSession;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Str;
|
|
|
|
class ViewAsController extends Controller
|
|
{
|
|
/**
|
|
* Start viewing as another user
|
|
*/
|
|
public function start(Request $request, string $businessSlug, int $userId)
|
|
{
|
|
try {
|
|
$business = currentBusiness();
|
|
|
|
if (! $business) {
|
|
return redirect()->back()->with('error', 'Business not found');
|
|
}
|
|
|
|
// Only owners and admins with view_as permission can impersonate
|
|
$canViewAs = auth()->user()->user_type === 'admin' ||
|
|
$business->owner_user_id === auth()->id() ||
|
|
hasBusinessPermission('users.view_as');
|
|
|
|
if (! $canViewAs) {
|
|
return redirect()->back()->with('error', 'You do not have permission to view as other users');
|
|
}
|
|
|
|
$targetUser = User::findOrFail($userId);
|
|
|
|
// Verify target user belongs to this business
|
|
if (! $targetUser->businesses()->where('businesses.id', $business->id)->exists()) {
|
|
return redirect()->back()->with('error', 'User does not belong to this business');
|
|
}
|
|
|
|
// Prevent viewing as business owner or other admins
|
|
if ($targetUser->user_type === 'admin' || $targetUser->id === $business->owner_user_id) {
|
|
return redirect()->back()->with('error', 'Cannot view as business owner or admin users');
|
|
}
|
|
|
|
// Prevent viewing as yourself
|
|
if ($targetUser->id === auth()->id()) {
|
|
return redirect()->back()->with('error', 'Cannot view as yourself');
|
|
}
|
|
|
|
// Check for maximum concurrent sessions
|
|
$maxConcurrent = config('permissions.view_as.max_concurrent_sessions', 3);
|
|
$activeSessions = ViewAsSession::forOriginalUser(auth()->id())
|
|
->forBusiness($business->id)
|
|
->active()
|
|
->count();
|
|
|
|
if ($activeSessions >= $maxConcurrent) {
|
|
return redirect()->back()->with('error', 'Maximum concurrent View As sessions reached. Please end an existing session first.');
|
|
}
|
|
|
|
// Generate unique session ID
|
|
$sessionId = Str::random(32);
|
|
|
|
// Create session record
|
|
$session = ViewAsSession::startSession(
|
|
businessId: $business->id,
|
|
originalUserId: auth()->id(),
|
|
viewingAsUserId: $targetUser->id,
|
|
sessionId: $sessionId
|
|
);
|
|
|
|
// Store in session
|
|
session([
|
|
'view_as_session_id' => $sessionId,
|
|
'view_as_user_id' => $targetUser->id,
|
|
'view_as_original_user_id' => auth()->id(),
|
|
]);
|
|
|
|
Log::info('Started View As session', [
|
|
'session_id' => $sessionId,
|
|
'business_id' => $business->id,
|
|
'original_user_id' => auth()->id(),
|
|
'viewing_as_user_id' => $targetUser->id,
|
|
]);
|
|
|
|
return redirect()
|
|
->route('seller.business.dashboard', $business->slug)
|
|
->with('success', "Now viewing as {$targetUser->name}");
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('Error starting View As session', [
|
|
'error' => $e->getMessage(),
|
|
'user_id' => $userId,
|
|
'business_slug' => $businessSlug,
|
|
]);
|
|
|
|
return redirect()->back()->with('error', 'Failed to start View As session');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* End View As session
|
|
*/
|
|
public function end(Request $request)
|
|
{
|
|
try {
|
|
$sessionId = session('view_as_session_id');
|
|
|
|
if (! $sessionId) {
|
|
return redirect()->back()->with('error', 'No active View As session');
|
|
}
|
|
|
|
// Find and end the session
|
|
$session = ViewAsSession::findActiveBySessionId($sessionId);
|
|
|
|
if ($session) {
|
|
$session->end();
|
|
|
|
Log::info('Ended View As session', [
|
|
'session_id' => $sessionId,
|
|
'duration_seconds' => $session->duration_seconds,
|
|
'pages_viewed' => $session->pages_viewed,
|
|
]);
|
|
}
|
|
|
|
// Clear session data
|
|
session()->forget([
|
|
'view_as_session_id',
|
|
'view_as_user_id',
|
|
'view_as_original_user_id',
|
|
]);
|
|
|
|
$business = currentBusiness();
|
|
|
|
return redirect()
|
|
->route('seller.business.users.index', $business?->slug ?? 'home')
|
|
->with('success', 'View As session ended');
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('Error ending View As session', [
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return redirect()->back()->with('error', 'Failed to end View As session');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current View As session info (for AJAX)
|
|
*/
|
|
public function status(Request $request)
|
|
{
|
|
$sessionId = session('view_as_session_id');
|
|
|
|
if (! $sessionId) {
|
|
return response()->json([
|
|
'active' => false,
|
|
]);
|
|
}
|
|
|
|
$session = ViewAsSession::findActiveBySessionId($sessionId);
|
|
|
|
if (! $session) {
|
|
return response()->json([
|
|
'active' => false,
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'active' => true,
|
|
'viewing_as_name' => $session->viewingAsUser?->name,
|
|
'original_user_name' => $session->originalUser?->name,
|
|
'started_at' => $session->started_at->toISOString(),
|
|
'duration' => $session->formatted_duration,
|
|
'remaining_time' => $session->remaining_time,
|
|
'pages_viewed' => $session->pages_viewed,
|
|
]);
|
|
}
|
|
}
|