feat(auth): HasRole middleware with OR-semantics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
986f2afcff
commit
a099f2a4dc
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HasRole
|
||||
{
|
||||
public function handle(Request $request, Closure $next, string ...$roles): mixed
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (!$user) {
|
||||
return redirect('/login');
|
||||
}
|
||||
|
||||
// OR semantics: comma-separated roles = ANY of them grants access
|
||||
foreach ($roles as $role) {
|
||||
if ($user->hasRole($role)) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,12 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->redirectTo(guests: '/login');
|
||||
|
||||
$middleware->alias([
|
||||
'role' => \App\Http\Middleware\HasRole::class,
|
||||
'license' => \App\Http\Middleware\EnsureActiveLicense::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
|
|
|||
|
|
@ -5,3 +5,8 @@ use Illuminate\Support\Facades\Route;
|
|||
Route::get('/', function () {
|
||||
return view('welcome');
|
||||
});
|
||||
|
||||
// Test route for middleware test — only reachable with school-admin or teacher role
|
||||
Route::middleware(['auth', 'role:school-admin,teacher'])->group(function () {
|
||||
Route::get('/school/dashboard', fn() => response('ok'))->name('school.dashboard');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use function Pest\Laravel\actingAs;
|
||||
use function Pest\Laravel\get;
|
||||
|
||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||
|
||||
it('blockt school-admin-Route für child-Rolle', function () {
|
||||
$child = User::factory()->withRole('child')->create();
|
||||
actingAs($child)->get('/school/dashboard')->assertForbidden();
|
||||
});
|
||||
|
||||
it('erlaubt school-admin-Route für teacher (OR-Semantik)', function () {
|
||||
$teacher = User::factory()->withRole('teacher')->create();
|
||||
actingAs($teacher)->get('/school/dashboard')->assertOk();
|
||||
});
|
||||
|
||||
it('erlaubt school-admin-Route für school-admin (OR-Semantik)', function () {
|
||||
$admin = User::factory()->withRole('school-admin')->create();
|
||||
actingAs($admin)->get('/school/dashboard')->assertOk();
|
||||
});
|
||||
|
||||
it('redirected Unauthenticated to login', function () {
|
||||
get('/school/dashboard')->assertRedirect('/login');
|
||||
});
|
||||
Loading…
Reference in New Issue