fix(tenant): forceDelete guard, hasRole eager-load, write-path test, migration timestamps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
1709f0f171
commit
986f2afcff
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue