Model Database & Dynamic Decoupling
Salah satu prinsip desain fundamental dari pasax/secure-monitor adalah Loose Coupling (keterikatan longgar). Paket ini tidak pernah mengasumsikan model User...
Salah satu prinsip desain fundamental dari pasax/secure-monitor adalah Loose Coupling (keterikatan longgar). Paket ini tidak pernah mengasumsikan model User bawaan Laravel atau nama tabel yang paten, melainkan menyelesaikan semuanya secara dinamis melalui konfigurasi.
1. Konvensi Nama Tabel Dinamis#
Semua model Eloquent paket meng-override metode getTable() untuk membaca nama tabel dari konfigurasi config/pasax.php dengan default berawalan pasax_:
public function getTable(): string
{
$configured = config('pasax.table_names.blocked_ips', 'pasax_blocked_ips');
return static::resolvePasaxTable('blocked_ips', $configured, 'blocked_ips', 'pasax_blocked_ips');
}
Hal ini memungkinkan Anda mengubah nama tabel di berkas .env jika terdapat benturan nama (table name conflict) dengan aplikasi eksisting Anda:
PASAX_BLOCKED_IPS_TABLE=pasax_blocked_ips
PASAX_LOGS_TABLE=pasax_logs
PASAX_LOGIN_ATTEMPTS_TABLE=pasax_login_attempts
PASAX_IP_UNBLOCK_REQUESTS_TABLE=pasax_ip_unblock_requests
PASAX_USER_LOGINS_TABLE=pasax_user_logins
PASAX_TRUSTED_IPS_TABLE=pasax_trusted_ips
PASAX_ALLOWED_IPS_TABLE=pasax_allowed_ips
2. Rincian Model Eloquent#
Kompatibilitas Penuh: Model baru bernama
Pasax*(PasaxBlockedIp,PasaxLog,PasaxLoginAttempt,PasaxIpUnblockRequest,PasaxUserLogin,PasaxTrustedIp,PasaxAllowedIp,PasaxSetting). Nama model lama (BlockedIp,SecurityLog,LoginAttempt, dll.) tetap disediakan sebagai class alias transparan sehingga proyek lama tidak terganggu.
A. `PasaxBlockedIp` (Alias: `BlockedIp`) (`Internal\Pasax\Models\PasaxBlockedIp`)#
Menyimpan data entitas yang sedang diblokir atau pernah diblokir. Default tabel: pasax_blocked_ips.
| Kolom | Tipe Data | Deskripsi |
|---|---|---|
id |
BigIncrements | Primary key |
ip_address |
String(45) | Alamat IPv4 atau IPv6 |
local_ip |
String(45), nullable | Alamat IP LAN lokal perangkat |
device_id |
String(100), nullable | UUID / Fingerprint perangkat |
block_scope |
Enum('ip', 'device') | Cakupan blokir: seluruh IP publik atau perangkat tertentu |
reason |
String(255) | Alasan pemblokiran (nama signature / catatan admin) |
notes |
Text, nullable | Catatan internal pengelola sistem |
source |
Enum('manual', 'auto', 'instant') | Asal pemblokiran (admin atau deteksi otomatis WAF) |
blocked_by |
BigInteger, nullable | ID user admin yang melakukan blokir (jika manual) |
blocked_at |
DateTime | Waktu pemblokiran dimulai |
expires_at |
DateTime, nullable | Waktu kedaluwarsa (null = blokir permanen) |
is_active |
Boolean | Status aktifasi blokir (true = diblokir, false = nonaktif) |
hit_count |
UnsignedBigInteger | Berapa kali entitas ini mencoba mengakses saat diblokir |
last_hit_at |
DateTime, nullable | Waktu percobaan akses terakhir saat terblokir |
Eloquent Scopes Tersedia:
BlockedIp::active(): Mengambil entri yangis_active = truedan belum kedaluwarsa (expires_at > now()atauexpires_at is null).BlockedIp::expired(): Mengambil entri yang masa berlakunya telah habis.BlockedIp::permanent(): Mengambil entri blokir yang permanen (expires_at is null).
B. `SecurityLog` (`Internal\Pasax\Models\SecurityLog`)#
Menyimpan rekam jejak setiap ancaman keamanan yang terdeteksi oleh WAF.
| Kolom | Tipe Data | Deskripsi |
|---|---|---|
id |
BigIncrements | Primary key |
event_type |
String(50) | Tipe kejadian: threat_detected, blocked_attempt, admin_bypass, dll. |
threat_level |
Enum('low', 'medium', 'high', 'critical') | Tingkat bahaya ancaman |
rule_id |
String(50) | Pengenal aturan (misal sql_injection, webshell_upload) |
rule_label |
String(100) | Label deskriptif bahasa manusia |
ip_address |
String(45) | IP klien |
device_id |
String(100), nullable | Fingerprint perangkat klien |
local_ip |
String(45), nullable | IP lokal perangkat |
user_id |
BigInteger, nullable | ID user yang terautentikasi (jika ada) |
user_agent |
Text, nullable | User-Agent klien |
method |
String(10) | HTTP Method (GET, POST, PUT, dll.) |
path |
String(255) | Request URI path |
query_string |
Text, nullable | Parameter URL mentah |
evidence |
Text, nullable | Potongan payload atau string bukti serangan |
was_blocked |
Boolean | Apakah request ini berujung pada penolakan akses |
action_taken |
String(50), nullable | Tindakan yang diambil (instant_block, auto_blocked, rejected) |
created_at |
DateTime | Waktu pencatatan |
C. `LoginAttempt` (`Internal\Pasax\Models\LoginAttempt`)#
Melacak kegagalan otentikasi untuk mitigasi brute force bertingkat (stepped lockout).
| Kolom | Tipe Data | Deskripsi |
|---|---|---|
id |
BigIncrements | Primary key |
email |
String(150), nullable | Email akun yang dicoba |
ip_address |
String(45) | IP sumber kegagalan |
device_id |
String(100), nullable | Fingerprint perangkat |
attempts |
Integer | Jumlah kegagalan berturut-turut pada tier saat ini |
level |
Integer | Tingkatan penalti lockout (1 s/d 6+) |
locked_until |
DateTime, nullable | Waktu berakhirnya penguncian |
last_attempt_at |
DateTime | Waktu percobaan gagal terakhir |
D. `IpUnblockRequest` (`Internal\Pasax\Models\IpUnblockRequest`)#
Menyimpan tiket banding yang diajukan oleh pengguna publik yang terblokir.
| Kolom | Tipe Data | Deskripsi |
|---|---|---|
id |
BigIncrements | Primary key |
ticket_number |
String(30), unique | Nomor tiket unik (format TKT-XXXXXXXX) |
ip_address |
String(45) | IP pemohon |
local_ip |
String(45), nullable | IP lokal pemohon |
device_id |
String(100), nullable | Device ID pemohon |
name |
String(100) | Nama lengkap pemohon |
email |
String(150) | Email aktif pemohon untuk konfirmasi |
phone |
String(30), nullable | Nomor kontak pemohon |
reason |
Text | Penjelasan alasan permohonan banding |
status |
Enum('pending', 'approved', 'rejected') | Status tiket saat ini |
admin_notes |
Text, nullable | Catatan dari administrator saat merespons |
resolved_by |
BigInteger, nullable | ID user administrator yang memproses |
resolved_at |
DateTime, nullable | Waktu penyelesaian tiket |
blocked_ip_id |
BigInteger, nullable | Foreign key ke tabel blocked_ips |
Eloquent Scopes: pending(), approved(), rejected().
E. `UserLogin` & `TrustedIp`#
UserLogin: Menyimpan riwayat setiap login yang berhasil, mencatat user agent yang diparse (nama browser, sistem operasi), sesi aktif (session_id), dan stempel waktu aktivitas terakhir (last_activity_at).- Scope
active(int $minutes = 5): Mengambil pengguna yang memiliki aktivitas dalam N menit terakhir (realtime online users).
- Scope
TrustedIp: Menyimpan daftar IP dan perangkat terpercaya per pengguna yang telah diverifikasi.
F. `PasaxSetting` (`Internal\Pasax\Models\PasaxSetting`)#
Menyimpan konfigurasi dinamis yang dikelola via Dashboard Pasax → Pengaturan (/pasax/settings) maupun API Admin. Default tabel: pasax_settings.
| Kolom | Tipe Data | Deskripsi |
|---|---|---|
id |
BigIncrements | Primary key |
key |
String(100), unique | Kunci identifikasi pengaturan (mis. auto_block.level_thresholds) |
value |
LongText, nullable | Nilai pengaturan (string skalar atau format JSON) |
description |
Text, nullable | Penjelasan fungsi pengaturan untuk administrator |
created_at |
DateTime | Waktu pembuatan entri |
updated_at |
DateTime | Waktu pembaruan terakhir |
Entri default diisi otomatis oleh PasaxDefaultSettingsSeeder saat pasax:install --migrate atau pasax:upgrade.
3. Integrasi Trait `HasPasaxSecurityRelations`#
Untuk menghubungkan model User aplikasi Anda dengan model-model di atas, cukup gunakan trait Internal\Pasax\Concerns\HasPasaxSecurityRelations:
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Internal\Pasax\Concerns\HasPasaxSecurityRelations;
class User extends Authenticatable
{
use HasPasaxSecurityRelations;
}
Catatan Kompatibilitas: Nama lama
HasSecurityRelationstetap didukung secara penuh melalui alias.
Metode yang tersedia:
$user = User::find(1);
// Riwayat sesi login (HasMany ke PasaxUserLogin)
$logins = $user->logins;
// Daftar IP terpercaya (HasMany ke PasaxTrustedIp)
$trustedIps = $user->trustedIps;
// Log keamanan yang terkait (HasMany ke PasaxLog)
$pasaxLogs = $user->pasaxLogs;
$securityLogs = $user->securityLogs; // alias kompatibilitas
// Allowlist IP yang didaftarkan (HasMany ke PasaxAllowedIp)
$allowedIps = $user->allowedIps;
// Entri IP yang diblokir oleh admin ini
$blockedByMe = $user->blockedIps;
// Tiket banding yang diselesaikan oleh admin ini
$resolvedTickets = $user->resolvedTickets;