feat: Separate admin and user tables for independent authentication
- Create new 'admins' table with role column (admin/super_admin) - Create Admin model for admin authentication - Add AdminSeeder with test data (super_admin, admin) - Update config/auth.php with dual guards (web for users, admin for admins) - Update AdminAuthController to use Admin model and 'admin' guard - Remove role column from users table (users have no role) - Update register validation messages in Indonesian - Now same email can be used by user and admin (separate tables)
This commit is contained in:
parent
cf41a8b582
commit
585d36fe09
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use App\Models\User;
|
use App\Models\Admin;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
class AdminAuthController extends Controller
|
class AdminAuthController extends Controller
|
||||||
|
|
@ -23,16 +23,8 @@ public function login(Request $request)
|
||||||
'password' => 'required'
|
'password' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// FIXED: Hapus guard('admin'), pakai default Auth
|
// Gunakan guard 'admin' - ambil dari tabel admins
|
||||||
if (Auth::attempt($credentials)) {
|
if (Auth::guard('admin')->attempt($credentials)) {
|
||||||
// Cek role - hanya admin dan super_admin yang boleh login
|
|
||||||
$user = Auth::user();
|
|
||||||
if (!in_array($user->role, ['admin', 'super_admin'])) {
|
|
||||||
Auth::logout();
|
|
||||||
$request->session()->invalidate();
|
|
||||||
return back()->with('error', 'Akses ditolak. Halaman ini khusus untuk pemilik bisnis.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->session()->regenerate();
|
$request->session()->regenerate();
|
||||||
return redirect()->intended(route('dashboard'));
|
return redirect()->intended(route('dashboard'));
|
||||||
}
|
}
|
||||||
|
|
@ -51,16 +43,25 @@ public function register(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'name' => 'required|string|max:50',
|
'name' => 'required|string|max:50',
|
||||||
'email' => 'required|email|unique:users,email',
|
'email' => 'required|email|unique:admins,email',
|
||||||
'password' => 'required|min:6|confirmed'
|
'password' => 'required|min:6|confirmed'
|
||||||
|
], [
|
||||||
|
'name.required' => 'Nama wajib diisi',
|
||||||
|
'name.max' => 'Nama maksimal 50 karakter',
|
||||||
|
'email.required' => 'Email wajib diisi',
|
||||||
|
'email.email' => 'Format email tidak valid',
|
||||||
|
'email.unique' => 'Email sudah terdaftar',
|
||||||
|
'password.required' => 'Password wajib diisi',
|
||||||
|
'password.min' => 'Password minimal 6 karakter',
|
||||||
|
'password.confirmed' => 'Konfirmasi password tidak cocok'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Simpan ke tabel users (ROLE SELALU admin - tidak bisa diubah user)
|
// Simpan ke tabel admins (ROLE SELALU admin)
|
||||||
User::create([
|
Admin::create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
'password' => Hash::make($request->password),
|
'password' => Hash::make($request->password),
|
||||||
'role' => 'admin' // PAKSA SELALU ADMIN - tidak bisa jadi super_admin lewat register
|
'role' => 'admin' // Hanya admin, tidak bisa super_admin
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.login')->with('success', 'Akun admin berhasil dibuat!');
|
return redirect()->route('admin.login')->with('success', 'Akun admin berhasil dibuat!');
|
||||||
|
|
@ -69,8 +70,7 @@ public function register(Request $request)
|
||||||
// Logout Admin
|
// Logout Admin
|
||||||
public function logout(Request $request)
|
public function logout(Request $request)
|
||||||
{
|
{
|
||||||
// FIXED: Hapus guard('admin'), pakai default Auth
|
Auth::guard('admin')->logout();
|
||||||
Auth::logout();
|
|
||||||
|
|
||||||
$request->session()->invalidate();
|
$request->session()->invalidate();
|
||||||
$request->session()->regenerateToken();
|
$request->session()->regenerateToken();
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ class AuthController extends Controller
|
||||||
public function register(RegisterRequest $request)
|
public function register(RegisterRequest $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
Log::info('Register attempt', ['email' => $request->email, 'name' => $request->name]);
|
||||||
|
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
|
|
@ -29,6 +31,8 @@ public function register(RegisterRequest $request)
|
||||||
|
|
||||||
$token = $user->createToken('mobile-app-token')->plainTextToken;
|
$token = $user->createToken('mobile-app-token')->plainTextToken;
|
||||||
|
|
||||||
|
Log::info('Registration successful', ['user_id' => $user->id]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Registrasi berhasil',
|
'message' => 'Registrasi berhasil',
|
||||||
|
|
@ -37,11 +41,28 @@ public function register(RegisterRequest $request)
|
||||||
'token' => $token
|
'token' => $token
|
||||||
]
|
]
|
||||||
], 201);
|
], 201);
|
||||||
} catch (\Exception $e) {
|
} catch (\Illuminate\Database\QueryException $e) {
|
||||||
Log::error('Registration error: ' . $e->getMessage());
|
Log::error('Database error during registration', [
|
||||||
|
'email' => $request->email,
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'sql' => $e->getSql() ?? 'N/A',
|
||||||
|
'bindings' => $e->getBindings() ?? []
|
||||||
|
]);
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Terjadi kesalahan saat registrasi',
|
'message' => 'Database error: ' . $e->getMessage(),
|
||||||
|
'error_code' => 'DB_ERROR',
|
||||||
|
], 500);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Registration error', [
|
||||||
|
'email' => $request->email,
|
||||||
|
'exception' => get_class($e),
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Terjadi kesalahan saat registrasi: ' . $e->getMessage(),
|
||||||
'error_code' => 'REGISTRATION_FAILED',
|
'error_code' => 'REGISTRATION_FAILED',
|
||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
|
class Admin extends Authenticatable
|
||||||
|
{
|
||||||
|
use HasFactory, Notifiable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'email',
|
||||||
|
'password',
|
||||||
|
'role',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that should be hidden for serialization.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $hidden = [
|
||||||
|
'password',
|
||||||
|
'remember_token',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attributes that should be cast.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email_verified_at' => 'datetime',
|
||||||
|
'password' => 'hashed',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
@ -12,7 +11,7 @@
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||||
use HasFactory, Notifiable, SoftDeletes, HasApiTokens;
|
use HasFactory, Notifiable, HasApiTokens;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|
@ -23,7 +22,6 @@ class User extends Authenticatable
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
'role',
|
|
||||||
'phone',
|
'phone',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'defaults' => [
|
'defaults' => [
|
||||||
'guard' => 'admin',
|
'guard' => 'web',
|
||||||
'passwords' => 'users',
|
'passwords' => 'users',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
'admin' => [
|
'admin' => [
|
||||||
'driver' => 'session',
|
'driver' => 'session',
|
||||||
'provider' => 'users', // ← PENTING!!
|
'provider' => 'admins', // ← Guard terpisah untuk admin
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -24,6 +24,11 @@
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => App\Models\User::class,
|
'model' => App\Models\User::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'admins' => [
|
||||||
|
'driver' => 'eloquent',
|
||||||
|
'model' => App\Models\Admin::class,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'passwords' => [
|
'passwords' => [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('admins', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('email')->unique();
|
||||||
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
|
$table->string('password');
|
||||||
|
$table->enum('role', ['admin', 'super_admin'])->default('admin'); // Pembeda admin vs super_admin
|
||||||
|
$table->rememberToken();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('admin_password_reset_tokens', function (Blueprint $table) {
|
||||||
|
$table->string('email')->primary();
|
||||||
|
$table->string('token');
|
||||||
|
$table->timestamp('created_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('admins');
|
||||||
|
Schema::dropIfExists('admin_password_reset_tokens');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Force delete soft-deleted users to clean up
|
||||||
|
DB::table('users')->whereNotNull('deleted_at')->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// Cannot restore deleted records
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Remove soft deletes from users table
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropSoftDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
// Hapus kolom role karena sekarang sudah ada di tabel admins
|
||||||
|
$table->dropColumn('role');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->enum('role', ['super_admin', 'admin', 'user'])->default('user')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Admin;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class AdminSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Buat super admin
|
||||||
|
Admin::create([
|
||||||
|
'name' => 'Super Admin',
|
||||||
|
'email' => 'super@admin.com',
|
||||||
|
'password' => Hash::make('password123'),
|
||||||
|
'role' => 'super_admin'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Buat admin biasa
|
||||||
|
Admin::create([
|
||||||
|
'name' => 'Admin',
|
||||||
|
'email' => 'admin@admin.com',
|
||||||
|
'password' => Hash::make('password123'),
|
||||||
|
'role' => 'admin'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue