Files
hub/app/Http/Controllers/ViewAsController.php
kelly 078e4f380c feat(analytics): Add missing permission/view-as controllers, views, and traits
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>
2025-11-14 21:07:44 -07:00

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,
]);
}
}