fix(tenant): forceDelete guard, hasRole eager-load, write-path test, migration timestamps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Boban Blaskovic 2026-05-22 01:07:26 +02:00
parent 1709f0f171
commit 986f2afcff
8 changed files with 30 additions and 3 deletions

View File

@ -26,7 +26,8 @@ class User extends Authenticatable
public function hasRole(string $role): bool public function hasRole(string $role): bool
{ {
return $this->role()->where('roles.name', $role)->exists(); $this->loadMissing('role');
return $this->role->contains('name', $role);
} }
public function tenant() public function tenant()
@ -58,7 +59,9 @@ class User extends Authenticatable
protected static function booted(): void protected static function booted(): void
{ {
static::deleting(function (User $user) { static::deleting(function (User $user) {
// Soft-delete cascade: DB foreign key cascades don't fire on soft-delete if ($user->isForceDeleting()) {
return; // FK cascadeOnDelete handles hard-delete
}
\DB::table('role_user')->where('user_id', $user->id)->delete(); \DB::table('role_user')->where('user_id', $user->id)->delete();
\DB::table('license_assignments')->where('user_id', $user->id)->delete(); \DB::table('license_assignments')->where('user_id', $user->id)->delete();
\DB::table('parent_child') \DB::table('parent_child')

View File

@ -3,6 +3,7 @@
use App\Models\Tenant; use App\Models\Tenant;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
uses(RefreshDatabase::class); uses(RefreshDatabase::class);
@ -12,6 +13,6 @@ it('verhindert Zugriff auf Daten anderer Tenants', function () {
$user1 = User::factory()->for($tenant1)->create(); $user1 = User::factory()->for($tenant1)->create();
$user2 = User::factory()->for($tenant2)->create(); $user2 = User::factory()->for($tenant2)->create();
$this->actingAs($user1); actingAs($user1);
expect(User::all()->pluck('id'))->not->toContain($user2->id); expect(User::all()->pluck('id'))->not->toContain($user2->id);
}); });

View File

@ -0,0 +1,23 @@
<?php
use App\Models\License;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
uses(RefreshDatabase::class);
it('BelongsToTenant überschreibt tenant_id bei non-admin Creation', function () {
$tenant1 = Tenant::factory()->create();
$tenant2 = Tenant::factory()->create();
$user = User::factory()->for($tenant1)->create();
actingAs($user);
$license = License::create([
'type' => 'per_student',
'tenant_id' => $tenant2->id, // attempt cross-tenant write
]);
expect($license->tenant_id)->toBe($tenant1->id);
});