diff --git a/app/Http/Controllers/Auth/AdminLoginController.php b/app/Http/Controllers/Auth/AdminLoginController.php new file mode 100644 index 0000000..9f41832 --- /dev/null +++ b/app/Http/Controllers/Auth/AdminLoginController.php @@ -0,0 +1,26 @@ +authenticate(); // Menjalankan logika ketat di LoginRequest + $request->session()->regenerate(); + return redirect()->route('admin.dashboard'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php index 8e2987d..e791894 100644 --- a/app/Http/Controllers/Auth/AuthenticatedSessionController.php +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -14,9 +14,14 @@ class AuthenticatedSessionController extends Controller /** * Display the login view. */ - public function create(): View + public function create(Request $request): View { - return view('auth.login'); + // Ambil 'role' dari URL, jika tidak ada, defaultnya 'siswa' + $role = $request->query('role', 'siswa'); + + return view('auth.login', [ + 'role' => $role + ]); } /** @@ -24,22 +29,12 @@ public function create(): View */ public function store(LoginRequest $request): RedirectResponse { - $request->authenticate(); - + $request->authenticate(); // Menjalankan logika di LoginRequest $request->session()->regenerate(); - // Ambil data user dari session - $userData = session('user_data'); - - // Cek role dan redirect sesuai role - if ($userData && isset($userData['role']) && $userData['role'] === 'penjaga perpus') { - return redirect()->route('admin.dashboard'); - } - - // Default redirect ke dashboard siswa - return redirect()->route('dashboard'); + // Karena login sudah dijamin benar, cukup arahkan ke dashboard umum + return redirect()->intended(route('dashboard')); } - /** * Destroy an authenticated session. */ @@ -47,11 +42,11 @@ public function destroy(Request $request): RedirectResponse { Auth::guard('web')->logout(); - $request->session()->forget('user_data'); + $request->session()->forget('user_data'); $request->session()->invalidate(); $request->session()->regenerateToken(); - + return redirect('/'); } -} \ No newline at end of file +} diff --git a/app/Http/Middleware/AuthenticateFromSessionData.php b/app/Http/Middleware/AuthenticateFromSessionData.php deleted file mode 100644 index dd1c09a..0000000 --- a/app/Http/Middleware/AuthenticateFromSessionData.php +++ /dev/null @@ -1,25 +0,0 @@ -has('user_data') && !Auth::check()) { - $userArray = session('user_data'); - $userArray['name'] = $userArray['nama_lengkap']; - - $userModel = new User(); - $userModel->forceFill($userArray); - - Auth::login($userModel); - } - return $next($request); - } -} \ No newline at end of file diff --git a/app/Http/Middleware/CheckRole.php b/app/Http/Middleware/CheckRole.php index 4498f31..c4953a6 100644 --- a/app/Http/Middleware/CheckRole.php +++ b/app/Http/Middleware/CheckRole.php @@ -4,25 +4,16 @@ use Closure; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class CheckRole { - /** - * Handle an incoming request. - */ - public function handle(Request $request, Closure $next, string $role): Response + public function handle(Request $request, Closure $next, ...$roles): Response { - $userData = session('user_data'); - - // Cek apakah user sudah login - if (!$userData) { - return redirect()->route('login'); - } - - // Cek apakah role sesuai - if (!isset($userData['role']) || $userData['role'] !== $role) { - abort(403, 'Akses ditolak. Anda tidak memiliki izin untuk mengakses halaman ini.'); + if (!Auth::check() || !in_array(Auth::user()->role, $roles)) { + return redirect()->route('login') + ->with('error', 'Akses ditolak. Anda tidak memiliki izin untuk mengakses halaman tersebut.'); } return $next($request); diff --git a/app/Http/Middleware/SessionAuthMiddleware.php b/app/Http/Middleware/SessionAuthMiddleware.php deleted file mode 100644 index d756f70..0000000 --- a/app/Http/Middleware/SessionAuthMiddleware.php +++ /dev/null @@ -1,23 +0,0 @@ -has('user_data')) { - return redirect()->route('login'); - } - - return $next($request); - } -} \ No newline at end of file diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php index 717d73b..4a9d1bb 100644 --- a/app/Http/Requests/Auth/LoginRequest.php +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -2,71 +2,133 @@ namespace App\Http\Requests\Auth; +use App\Models\User; use App\Services\DummyDataService; -use Illuminate\Auth\Events\Lockout; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; +use Illuminate\Auth\Events\Lockout; class LoginRequest extends FormRequest { /** - * Determine if the user is authorized to make this request. + * Menentukan apakah pengguna diizinkan untuk membuat request ini. + * Selalu true karena semua orang boleh mencoba login. */ public function authorize(): bool { return true; } + /** + * Mendapatkan aturan validasi yang berlaku untuk request ini. + * Aturan ini dinamis, berubah tergantung pada 'role' yang dikirim dari form. + */ public function rules(): array { + // Jika form mengirim 'role' dengan nilai 'siswa'... + if ($this->input('role') === 'siswa') { + // ...maka validasi input 'nisn' dan 'password'. + return [ + 'nisn' => ['required', 'string'], + 'password' => ['required', 'string'], + ]; + } + + // Jika tidak (untuk 'guru' dan 'penjaga perpus'), validasi 'email' dan 'password'. return [ - 'nisn' => ['required', 'string'], + 'email' => ['required', 'string', 'email'], 'password' => ['required', 'string'], ]; } + /** + * Mencoba untuk mengautentikasi kredensial dari request. + * Ini adalah "otak" dari proses login yang berisi logika paling penting. + * + * @throws \Illuminate\Validation\ValidationException + */ public function authenticate(): void { + // Langkah 1: Pastikan pengguna tidak mencoba login terlalu sering (mencegah brute-force). $this->ensureIsNotRateLimited(); - $allSiswa = DummyDataService::getAllSiswa(); - $inputNisn = $this->input('nisn'); + // Ambil data yang dikirim dari form login. + $roleDariForm = $this->input('role'); + $allUsers = DummyDataService::getAllSiswa(); $inputPassword = $this->input('password'); + $userArray = null; + + // Tentukan field mana yang akan menerima pesan error jika gagal (nisn atau email). + $errorField = $this->filled('nisn') ? 'nisn' : 'email'; - $userArray = collect($allSiswa)->firstWhere('nisn', $inputNisn); - - if ($userArray && $userArray['password'] === $inputPassword) { - // Simpan ke session - session(['user_data' => $userArray]); - - // Set redirect intention berdasarkan role - if (isset($userArray['role']) && $userArray['role'] === 'penjaga perpus') { - session()->put('url.intended', route('admin.dashboard')); - } - - RateLimiter::clear($this->throttleKey()); - return; + // Langkah 2: Cari data pengguna berdasarkan input yang diberikan. + if ($this->filled('nisn')) { + // Jika form diisi dengan 'nisn', cari pengguna berdasarkan 'nisn'. + $userArray = collect($allUsers)->firstWhere('nisn', $this->input('nisn')); + } else { + // Jika tidak, cari pengguna berdasarkan 'email'. + $userArray = collect($allUsers)->firstWhere('email', $this->input('email')); } + // Langkah 3: Lakukan Pengecekan Kredensial dan Role. + // Cek #1: Apakah pengguna ditemukan DAN password yang dimasukkan cocok? + if ($userArray && $userArray['password'] === $inputPassword) { + + // Cek #2: Jika kredensial benar, apakah role pengguna sesuai dengan form yang digunakan? + if (isset($userArray['role']) && $userArray['role'] === $roleDariForm) { + + // --- SEMUA SYARAT TERPENUHI --- + // Buat objek User dari data dummy. + $userModel = new User(); + $userArray['name'] = $userArray['nama_lengkap']; + $userModel->forceFill($userArray); + + // Loginkan pengguna secara resmi ke dalam sistem. + Auth::login($userModel); + + // Reset hitungan percobaan login yang gagal. + RateLimiter::clear($this->throttleKey()); + return; // Proses autentikasi berhasil. + + } else { + // --- KASUS GAGAL: KREDENSIAL BENAR, TAPI ROLE SALAH --- + // Tambah hitungan percobaan login yang gagal. + RateLimiter::hit($this->throttleKey()); + + // Ambil nama role asli pengguna untuk ditampilkan di pesan error. + $actualRole = Str::title($userArray['role'] ?? 'Tidak Dikenal'); + + // Lemparkan error validasi khusus 'forbidden' dengan pesan yang jelas. + throw ValidationException::withMessages([ + 'forbidden' => "Akses ditolak. Akun ini terdaftar sebagai {$actualRole}.", + ]); + } + } + + // --- KASUS GAGAL: KREDENSIAL TIDAK COCOK --- + // Jika pengguna tidak ditemukan atau password salah. RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ - 'nisn' => trans('auth.failed'), + $errorField => trans('auth.failed'), // Pesan error umum "These credentials do not match...". ]); } + /** + * Memastikan request login tidak dibatasi karena terlalu banyak percobaan. + */ public function ensureIsNotRateLimited(): void { - if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + // Jika percobaan belum melebihi 5 kali, lanjutkan. + if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } + // Jika sudah lebih dari 5 kali, lemparkan error 'throttle'. event(new Lockout($this)); - $seconds = RateLimiter::availableIn($this->throttleKey()); - throw ValidationException::withMessages([ 'email' => trans('auth.throttle', [ 'seconds' => $seconds, @@ -76,10 +138,13 @@ public function ensureIsNotRateLimited(): void } /** - * Get the rate limiting throttle key for the request. + * Mendapatkan kunci throttle untuk request ini. + * Kunci ini unik untuk setiap pengguna (berdasarkan nisn/email) dan alamat IP. */ public function throttleKey(): string { - return Str::transliterate(Str::lower($this->string('nisn')).'|'.$this->ip()); + // Gunakan 'nisn' jika ada, jika tidak, gunakan 'email' sebagai identitas. + $loginIdentifier = $this->input('nisn') ?: $this->input('email'); + return Str::transliterate(Str::lower($loginIdentifier) . '|' . $this->ip()); } } \ No newline at end of file diff --git a/app/Services/DummyDataService.php b/app/Services/DummyDataService.php index 3721fc5..cd00f5f 100644 --- a/app/Services/DummyDataService.php +++ b/app/Services/DummyDataService.php @@ -23,10 +23,8 @@ public static function getAllSiswa(): array ], [ 'id' => 2, - 'nisn' => '1122334455', 'nama_lengkap' => 'Budi Santoso', - 'email' => 'budi.santoso@smkn1perpus.sch.id', - 'nomor_hp' => '081122334455', + 'email' => 'budi.santoso@smkn1perpus.sch.id', 'password' => 'password', 'role' => 'penjaga perpus', ], @@ -54,10 +52,8 @@ public static function getAllSiswa(): array ], [ 'id' => 5, - 'nisn' => '2233445566', 'nama_lengkap' => 'Rina Marlina', - 'email' => 'rina.marlina@smkn1perpus.sch.id', - 'nomor_hp' => '081223344556', + 'email' => 'rina.marlina@smkn1perpus.sch.id', 'password' => 'password', 'role' => 'guru', ], diff --git a/bootstrap/app.php b/bootstrap/app.php index cf1a258..4d99605 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,14 +11,10 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware) { - $middleware->web(append: [ - \App\Http\Middleware\AuthenticateFromSessionData::class, - ]); - $middleware->alias([ - 'session.auth' => \App\Http\Middleware\SessionAuthMiddleware::class, - 'role' => \App\Http\Middleware\CheckRole::class, - ]); - }) + $middleware->alias([ + 'role' => \App\Http\Middleware\CheckRole::class, + ]); +}) ->withProviders([ App\Providers\AuthServiceProvider::class, ]) diff --git a/resources/scss/_variables.scss b/resources/scss/_variables.scss index 78b2707..4a126f6 100644 --- a/resources/scss/_variables.scss +++ b/resources/scss/_variables.scss @@ -317,3 +317,29 @@ $transition: all 0.3s ease; .book-option[style*="display: none"] { display: none !important; } + +// =================================== +// WELCOME PAGE & Login Page Styles +// =================================== + +.hero-gradient { + background: linear-gradient(135deg, map-get($theme-colors, "primary") 0%, color.adjust(map-get($theme-colors, "primary"), $lightness: 10%) 100%); +} + +.role-card { + display: block; + + .card { + transition: $transition; + border-radius: 20px; + + &:hover { + transform: translateY(-4px); + box-shadow: $shadow-md; + } + } +} + +.info-panel { + background: linear-gradient(135deg, map-get($theme-colors, "primary") 0%, color.adjust(map-get($theme-colors, "primary"), $lightness: 10%) 100%); +} diff --git a/resources/views/auth/admin-login.blade.php b/resources/views/auth/admin-login.blade.php new file mode 100644 index 0000000..2f66ae2 --- /dev/null +++ b/resources/views/auth/admin-login.blade.php @@ -0,0 +1,40 @@ + + @if ($errors->has('forbidden')) + + @endif + + + +
+ @csrf + + + +
+ +

Login Petugas

+

Halaman ini khusus untuk Penjaga Perpustakaan.

+
+ +
+ + + +
+ +
+ + +
+ +
+ +
+

+ Kembali ke halaman utama. +

+
+
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 81dfc06..2fd6683 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -1,75 +1,53 @@ - +@if ($errors->has('forbidden')) + + @endif
@csrf +
-

Login Siswa

-

Masukan NISN dan kata sandi Anda.

+ {{-- Judul dinamis --}} +

Login {{ Str::title($role) }}

+

+ @if ($role == 'siswa') + Masukan NISN dan kata sandi Anda. + @else + Masukan Email dan kata sandi Anda. + @endif +

-
- - - @error('nisn') -
{{ $message }}
- @enderror -
+ {{-- Form dinamis --}} + @if($role == 'siswa') +
+ + + {{-- <-- Pastikan ini ada --}} +
+ @else +
+ + + {{-- <-- Pastikan ini ada --}} +
+ @endif
-
- - - - - @error('password') -
{{ $message }}
- @enderror -
+
-
- @if (Route::has('password.request')) - - Lupa password? - - @endif -
- -
- +
+

- Belum punya akun? - Daftar sekarang + Kembali ke halaman utama.

- diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 45c89dc..11cbe97 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,6 +1,6 @@ @section('page-title', 'Dashboard') - + {{--