initial: bootstrap from BukidBountyApp base
This commit is contained in:
119
app/Models/PersonalAccessToken.php
Normal file
119
app/Models/PersonalAccessToken.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user