255 lines
8.6 KiB
PHP
255 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\App;
|
|
use App\Models\Onboarding;
|
|
use App\Models\Role;
|
|
use App\Models\Setting;
|
|
use App\Models\User;
|
|
use App\Models\UserAppOverride;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Str;
|
|
|
|
class OnboardingController extends Controller
|
|
{
|
|
/**
|
|
* Display the Onboarding & Offboarding Dashboard.
|
|
*/
|
|
public function index()
|
|
{
|
|
$user = Auth::user();
|
|
|
|
if (!$user->canManageOnboarding()) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied. You do not have permission to access Onboarding & Offboarding.');
|
|
}
|
|
|
|
$onboardings = Onboarding::with(['user', 'creator', 'offboarder'])
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
$activeUsers = User::with(['roles', 'overrides.app'])
|
|
->where('id', '!=', Auth::id())
|
|
->orderBy('name', 'asc')
|
|
->get();
|
|
|
|
$apps = App::orderBy('name', 'asc')->get();
|
|
$roles = Role::where('name', '!=', 'Admin')->orderBy('name', 'asc')->get();
|
|
|
|
$stats = [
|
|
'preboarding_count' => $onboardings->where('status', 'preboarding')->count(),
|
|
'active_interns_count' => $onboardings->where('type', 'intern')->where('status', 'active')->count(),
|
|
'offboarded_count' => $onboardings->where('status', 'offboarded')->count(),
|
|
'total_managed' => $onboardings->count(),
|
|
];
|
|
|
|
return view('onboarding.index', compact('onboardings', 'activeUsers', 'apps', 'roles', 'stats'));
|
|
}
|
|
|
|
/**
|
|
* Store a new onboarding candidate or intern profile.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$user = Auth::user();
|
|
if (!$user->canManageOnboarding()) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'email' => 'required|email|max:255',
|
|
'type' => 'required|string|in:intern,candidate,employee',
|
|
'join_date' => 'nullable|date',
|
|
'department' => 'nullable|string|max:255',
|
|
'assigned_roles' => 'nullable|array',
|
|
'assigned_apps' => 'nullable|array',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
$defaultChecklist = [
|
|
'document_verified' => false,
|
|
'email_created' => false,
|
|
'laptop_assigned' => false,
|
|
'sso_bundle_granted' => false,
|
|
'welcome_briefing' => false,
|
|
];
|
|
|
|
Onboarding::create([
|
|
'name' => $request->name,
|
|
'email' => strtolower(trim($request->email)),
|
|
'type' => $request->type,
|
|
'status' => 'preboarding',
|
|
'join_date' => $request->join_date,
|
|
'department' => $request->department,
|
|
'assigned_roles' => $request->assigned_roles ?? [],
|
|
'assigned_apps' => $request->assigned_apps ?? [],
|
|
'checklist' => $defaultChecklist,
|
|
'notes' => $request->notes,
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
|
|
return redirect()->route('onboarding.index')->with('success', 'Onboarding profile for ' . $request->name . ' created successfully.');
|
|
}
|
|
|
|
/**
|
|
* Activate Day-1 Candidate / Intern account & provision SSO access.
|
|
*/
|
|
public function activate($id)
|
|
{
|
|
$user = Auth::user();
|
|
if (!$user->canManageOnboarding()) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$onboarding = Onboarding::findOrFail($id);
|
|
|
|
// Check if user account already exists by email
|
|
$existingUser = User::where('email', $onboarding->email)->first();
|
|
|
|
if (!$existingUser) {
|
|
// Create user account with randomized initial password
|
|
$existingUser = User::create([
|
|
'name' => $onboarding->name,
|
|
'email' => $onboarding->email,
|
|
'password' => Hash::make(Str::random(16)),
|
|
'role' => 'user',
|
|
]);
|
|
}
|
|
|
|
// Attach assigned roles if any
|
|
if (!empty($onboarding->assigned_roles)) {
|
|
$existingUser->roles()->syncWithoutDetaching($onboarding->assigned_roles);
|
|
} else {
|
|
// Default role fallback
|
|
$defaultRoleName = Setting::get('default_role', 'Developer');
|
|
$defaultRole = Role::firstOrCreate(['name' => $defaultRoleName]);
|
|
$existingUser->roles()->syncWithoutDetaching([$defaultRole->id]);
|
|
}
|
|
|
|
// Attach pre-approved service overrides
|
|
if (!empty($onboarding->assigned_apps)) {
|
|
foreach ($onboarding->assigned_apps as $appId) {
|
|
UserAppOverride::firstOrCreate([
|
|
'user_id' => $existingUser->id,
|
|
'app_id' => $appId,
|
|
'type' => 'allow',
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Update onboarding record
|
|
$checklist = $onboarding->checklist ?? [];
|
|
$checklist['sso_bundle_granted'] = true;
|
|
|
|
$onboarding->update([
|
|
'user_id' => $existingUser->id,
|
|
'status' => 'active',
|
|
'checklist' => $checklist,
|
|
]);
|
|
|
|
return redirect()->route('onboarding.index')->with('success', 'Account activated for ' . $onboarding->name . '. SSO access provisioned!');
|
|
}
|
|
|
|
/**
|
|
* 1-Click Offboard: Instantly block account, revoke SSO apps, & log offboarding.
|
|
*/
|
|
public function offboard(Request $request, $id)
|
|
{
|
|
$user = Auth::user();
|
|
if (!$user->canManageOnboarding()) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$onboarding = Onboarding::find($id);
|
|
|
|
if ($onboarding && $onboarding->user_id) {
|
|
$targetUser = User::find($onboarding->user_id);
|
|
} else {
|
|
// Try to find user directly if $id is user_id
|
|
$targetUser = User::find($id);
|
|
if ($targetUser) {
|
|
$onboarding = Onboarding::firstOrCreate([
|
|
'user_id' => $targetUser->id,
|
|
], [
|
|
'name' => $targetUser->name,
|
|
'email' => $targetUser->email,
|
|
'type' => 'employee',
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
if (!$targetUser) {
|
|
return redirect()->route('onboarding.index')->with('error', 'Target user account not found.');
|
|
}
|
|
|
|
// Prevent offboarding self or admins
|
|
if ($targetUser->id === Auth::id() || $targetUser->isAdmin()) {
|
|
return redirect()->route('onboarding.index')->with('error', 'Security alert: You cannot offboard administrator accounts.');
|
|
}
|
|
|
|
// 1. Block user account
|
|
$targetUser->update(['is_blocked' => true]);
|
|
|
|
// 2. Clear active overrides / grants
|
|
$targetUser->overrides()->delete();
|
|
|
|
// 3. Update onboarding record
|
|
$checklist = $onboarding->checklist ?? [];
|
|
$checklist['exit_revocation_completed'] = true;
|
|
|
|
$onboarding->update([
|
|
'status' => 'offboarded',
|
|
'exit_date' => now(),
|
|
'offboarded_by' => Auth::id(),
|
|
'offboarded_at' => now(),
|
|
'checklist' => $checklist,
|
|
]);
|
|
|
|
return redirect()->route('onboarding.index')->with('success', '1-Click Offboarding completed for ' . $targetUser->name . '. Account blocked & SSO access revoked!');
|
|
}
|
|
|
|
/**
|
|
* Update checklist item for an onboarding / offboarding profile.
|
|
*/
|
|
public function updateChecklist(Request $request, $id)
|
|
{
|
|
$user = Auth::user();
|
|
if (!$user->canManageOnboarding()) {
|
|
return response()->json(['error' => 'Access denied'], 403);
|
|
}
|
|
|
|
$onboarding = Onboarding::findOrFail($id);
|
|
$checklist = $onboarding->checklist ?? [];
|
|
|
|
$key = $request->input('key');
|
|
$val = $request->boolean('val');
|
|
|
|
if ($key) {
|
|
$checklist[$key] = $val;
|
|
$onboarding->update(['checklist' => $checklist]);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'checklist' => $checklist]);
|
|
}
|
|
|
|
/**
|
|
* Delete an onboarding profile.
|
|
*/
|
|
public function destroy($id)
|
|
{
|
|
$user = Auth::user();
|
|
if (!$user->canManageOnboarding()) {
|
|
return redirect()->route('dashboard')->with('error', 'Access denied.');
|
|
}
|
|
|
|
$onboarding = Onboarding::findOrFail($id);
|
|
$onboarding->delete();
|
|
|
|
return redirect()->route('onboarding.index')->with('success', 'Onboarding record removed successfully.');
|
|
}
|
|
}
|