Files
BarangaySystem/app/Models/PersonalAccessToken.php
2026-06-06 18:43:00 +08:00

120 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models;
use App\Support\TokenAbilities;
use Hyperf\Database\Model\Relations\MorphTo;
class PersonalAccessToken extends Model
{
protected ?string $table = 'personal_access_tokens';
public bool $incrementing = true;
protected array $fillable = [
'tokenable_type',
'tokenable_id',
'name',
'description',
'token',
'abilities',
'allowed_ips',
'last_used_at',
'last_used_ip',
'expires_at',
'created_by',
'revoked_by',
'revoked_at',
];
protected array $casts = [
'abilities' => 'array',
'allowed_ips' => 'array',
'last_used_at' => 'datetime',
'expires_at' => 'datetime',
'revoked_at' => 'datetime',
];
protected array $hidden = [
'token',
];
public function tokenable(): MorphTo
{
return $this->morphTo();
}
public static function findByPlainToken(string $plain): ?self
{
return static::query()->where('token', hash('sha256', $plain))->first();
}
public function isExpired(): bool
{
return $this->expires_at !== null && $this->expires_at->isPast();
}
public function isRevoked(): bool
{
return $this->revoked_at !== null;
}
public function isActive(): bool
{
return ! $this->isExpired() && ! $this->isRevoked();
}
public function can(string $ability): bool
{
$abilities = $this->abilities ?? [];
if (in_array(TokenAbilities::WILDCARD, $abilities, true)) {
return true;
}
return in_array($ability, $abilities, true);
}
public function ipAllowed(string $ip): bool
{
$list = $this->allowed_ips ?? [];
if (empty($list)) {
return true;
}
foreach ($list as $entry) {
if ($this->ipMatches($ip, $entry)) {
return true;
}
}
return false;
}
private function ipMatches(string $ip, string $entry): bool
{
$entry = trim($entry);
if ($entry === '') {
return false;
}
if (! str_contains($entry, '/')) {
return $ip === $entry;
}
[$subnet, $bits] = explode('/', $entry, 2);
$bits = (int) $bits;
$ipBin = @inet_pton($ip);
$subnetBin = @inet_pton($subnet);
if ($ipBin === false || $subnetBin === false || strlen($ipBin) !== strlen($subnetBin)) {
return false;
}
$bytes = intdiv($bits, 8);
$remainder = $bits % 8;
if (substr($ipBin, 0, $bytes) !== substr($subnetBin, 0, $bytes)) {
return false;
}
if ($remainder === 0) {
return true;
}
$mask = chr(0xff << (8 - $remainder) & 0xff);
return (ord($ipBin[$bytes]) & ord($mask)) === (ord($subnetBin[$bytes]) & ord($mask));
}
}