Skip to content

Commit

Permalink
Add admin roles
Browse files Browse the repository at this point in the history
Add IsAdminMiddleware
Add super admin and admin to the seeder
Add test dashboard for admin and user
Update tests that check role permission
Update routes
  • Loading branch information
PrabuckiDominik committed Aug 21, 2024
1 parent 5826b80 commit b505b85
Show file tree
Hide file tree
Showing 20 changed files with 351 additions and 88 deletions.
16 changes: 16 additions & 0 deletions app/Http/Controllers/AdminDashboardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Inertia\Inertia;
use Inertia\Response;

class AdminDashboardController extends Controller
{
public function index(): Response
{
return Inertia::render("Auth/Admin/Dashboard");
}
}
6 changes: 5 additions & 1 deletion app/Http/Controllers/AuthenticateSessionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ public function authenticate(AuthenticateSessionRequest $request): RedirectRespo
if (auth()->attempt($credentials)) {
$request->session()->regenerate();

return Redirect::route("home")->with("success");
if (auth()->user()->isAdmin()) {
return Redirect::route("admin.dashboard")->with("success", "Welcome to the Admin Dashboard");
}

return Redirect::route("dashboard")->with("success");
}

throw ValidationException::withMessages([
Expand Down
5 changes: 5 additions & 0 deletions app/Http/Controllers/ContestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ public function index(): Response

return Inertia::render("Home", ["schools" => SchoolResource::collection($schools)]);
}

public function create(): Response
{
return Inertia::render("Dashboard");
}
}
2 changes: 0 additions & 2 deletions app/Http/Controllers/RegisterUserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;

Expand All @@ -23,7 +22,6 @@ public function store(RegisterUserRequest $request): RedirectResponse
$user->password = Hash::make($request->password);
$user->save();
event(new Registered($user));
Auth::login($user);
}

return Redirect::route("home");
Expand Down
20 changes: 20 additions & 0 deletions app/Http/Middleware/IsAdminMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use App\Role;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class IsAdminMiddleware
{
public function handle(Request $request, Closure $next): Response
{
abort_if($request->user()->role === Role::USER, Response::HTTP_FORBIDDEN);

return $next($request);
}
}
14 changes: 13 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Models;

use App\Role;
use Carbon\Carbon;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Contracts\Auth\MustVerifyEmail;
Expand All @@ -20,9 +21,9 @@
* @property string $password
* @property Carbon $email_verified_at
* @property int $school_id
* @property Role $role
* @property Carbon $created_at
* @property Carbon $updated_at
*
* @property School $school
*/
class User extends Authenticatable implements MustVerifyEmail, CanResetPassword
Expand All @@ -45,11 +46,22 @@ public function school(): BelongsTo
return $this->belongsTo(School::class);
}

public function isAdmin(): bool
{
return $this->role === Role::ADMIN || $this->role === Role::SUPER_ADMIN;
}

public function isSuperAdmin(): bool
{
return $this->role === Role::SUPER_ADMIN;
}

protected function casts(): array
{
return [
"email_verified_at" => "datetime",
"password" => "hashed",
"role" => Role::class,
];
}
}
4 changes: 4 additions & 0 deletions app/Policies/QuizPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public function update(User $user, Quiz $quiz): bool

public function delete(User $user, Quiz $quiz): bool
{
if ($user->isSuperAdmin()) {
return true;
}

return !$quiz->isLocked;
}

Expand Down
12 changes: 12 additions & 0 deletions app/Role.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace App;

enum Role: int
{
case USER = 0;
case ADMIN = 1;
case SUPER_ADMIN = 2;
}
13 changes: 11 additions & 2 deletions database/factories/UserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\Models\School;
use App\Models\User;
use App\Role;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
Expand All @@ -26,13 +27,21 @@ public function definition(): array
"password" => Hash::make("password"),
"remember_token" => Str::random(10),
"school_id" => School::factory(),
"role" => Role::USER->value,
];
}

public function unverified(): static
public function admin(): static
{
return $this->state(fn(array $attributes): array => [
"email_verified_at" => null,
"role" => Role::ADMIN->value,
]);
}

public function superAdmin(): static
{
return $this->state(fn(array $attributes): array => [
"role" => Role::SUPER_ADMIN->value,
]);
}
}
24 changes: 24 additions & 0 deletions database/migrations/2024_08_21_072609_add_role_to_users_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

use App\Role;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class() extends Migration {
public function up(): void
{
Schema::table("users", function (Blueprint $table): void {
$table->unsignedTinyInteger("role")->default(Role::USER->value);
});
}

public function down(): void
{
Schema::table("users", function (Blueprint $table): void {
$table->dropColumn("role");
});
}
};
3 changes: 2 additions & 1 deletion database/seeders/DatabaseSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class DatabaseSeeder extends Seeder
{
public function run(): void
{
User::factory()->create();
User::factory()->superAdmin()->create();
User::factory()->admin()->create();
Quiz::factory()->create();
Answer::factory()->create();
AnswerRecord::factory()->create();
Expand Down
13 changes: 13 additions & 0 deletions resources/js/Pages/Auth/Admin/Dashboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
</script>

<template>
<div>
Admin Dashboard
</div>
</template>

<style scoped>
</style>
11 changes: 11 additions & 0 deletions resources/js/Pages/Dashboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>

<template>
<div> User Dashboard </div>
</template>

<style scoped>
</style>
1 change: 1 addition & 0 deletions resources/js/components/Common/Searchbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const onOptionClick = (school:School)=>{
</div>

<input
autocomplete="off"
:value="isSearchFocused ? searchQuery : selected?.name"
class="outline-none py-3 bg-transparent w-full text-gray-900"
required name="school_id" :class="{'cursor-pointer' : !isSearchFocused}"
Expand Down
10 changes: 9 additions & 1 deletion routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare(strict_types=1);

use App\Http\Controllers\AdminDashboardController;
use App\Http\Controllers\AuthenticateSessionController;
use App\Http\Controllers\ContestController;
use App\Http\Controllers\EmailVerifyController;
Expand All @@ -12,6 +13,7 @@
use App\Http\Controllers\QuizSubmissionController;
use App\Http\Controllers\RegisterUserController;
use App\Http\Middleware\EnsureQuizIsNotAlreadyStarted;
use App\Http\Middleware\IsAdminMiddleware;
use App\Models\Answer;
use App\Models\Question;
use Illuminate\Support\Facades\Route;
Expand All @@ -30,9 +32,13 @@
Route::get("/auth/password/reset/{token}", [PasswordResetLinkController::class, "resetCreate"])->name("password.reset");
});

Route::middleware("auth")->group(function (): void {
Route::get("/dashboard", [ContestController::class, "create"])->name("dashboard");
});

Route::post("/auth/password/reset", [PasswordResetLinkController::class, "resetStore"])->name("password.update");

Route::group(["prefix" => "admin"], function (): void {
Route::group(["prefix" => "admin", "middleware" => ["auth", IsAdminMiddleware::class]], function (): void {
Route::get("/quizzes", [QuizController::class, "index"])->name("admin.quizzes.index");
Route::post("/quizzes", [QuizController::class, "store"])->name("admin.quizzes.store");
Route::get("/quizzes/{quiz}", [QuizController::class, "show"])->name("admin.quizzes.show");
Expand All @@ -55,6 +61,8 @@
Route::post("/answers/{answer}/clone/{question}", [QuestionAnswerController::class, "clone"])->can("clone,answer,question")->name("admin.answers.clone");
Route::post("/answers/{answer}/correct", [QuestionAnswerController::class, "markAsCorrect"])->can("update,answer")->name("admin.answers.correct");
Route::post("/answers/{answer}/invalid", [QuestionAnswerController::class, "markAsInvalid"])->can("update,answer")->name("admin.answers.invalid");

Route::get("/dashboard", [AdminDashboardController::class, "index"])->name("admin.dashboard");
});

Route::post("/quizzes/{quiz}/start", [QuizController::class, "createSubmission"])->middleware(EnsureQuizIsNotAlreadyStarted::class)->can("submit,quiz")->name("quizzes.start");
Expand Down
Loading

0 comments on commit b505b85

Please sign in to comment.