feat: implement multi-tenancy and super admin impersonation with security banner

This commit is contained in:
jeremy bayse
2026-02-21 20:15:47 +01:00
parent a0e904d69d
commit 63e448ef22
31 changed files with 819 additions and 51 deletions

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('agents', function (Blueprint $table) {
$table->foreignId('structure_id')->nullable()->constrained()->onDelete('cascade');
});
// Rattacher les agents existants au CABM
\Illuminate\Support\Facades\DB::table('agents')->whereNull('structure_id')->update(['structure_id' => 1]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('agents', function (Blueprint $table) {
$table->dropForeign(['structure_id']);
$table->dropColumn('structure_id');
});
}
};

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('structures', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('domain')->nullable()->unique();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('structures');
}
};

View File

@@ -0,0 +1,46 @@
<?php
namespace Database\Seeders;
use App\Models\Structure;
use App\Models\User;
use App\Models\Role;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
class SaaSTenantSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// 1. Créer la structure principale (votre agence CABM par défaut)
$cabm = Structure::firstOrCreate([
'slug' => 'cabm'
], [
'name' => 'CABM',
'is_active' => true,
]);
// 2. Mettre à jour tous les agents orphelins vers cette structure
DB::table('agents')->whereNull('structure_id')->update(['structure_id' => $cabm->id]);
// 3. Mettre à jour tous les services, templates, users, etc. vers cette structure
$tables = ['services', 'integration_templates', 'integration_requests', 'users', 'service_tasks'];
foreach ($tables as $table) {
if (Schema::hasColumn($table, 'structure_id')) {
DB::table($table)->whereNull('structure_id')->update(['structure_id' => $cabm->id]);
}
}
// 4. S'assurer que le rôle SuperAdmin existe globalement
$superAdminRole = Role::withoutGlobalScope('structure')->updateOrCreate(
['name' => 'SuperAdmin'],
['guard_name' => 'web', 'structure_id' => null]
);
$this->command->info('Migration vers le mode SaaS terminée. Structure par défaut : CABM.');
}
}