Files
php-auth/src/OAuth/Entities/Client.php
Ron Rise a1d7512ebc
Some checks failed
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 2m23s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 2m35s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 2m25s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 2m39s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Failing after 2m26s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Failing after 1m5s
Add audit logging functionality with database schema and event handling
2026-01-10 09:51:35 -05:00

236 lines
6.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Siteworxpro\App\OAuth\Entities;
use Defuse\Crypto\Exception\BadFormatException;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Key;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use Random\RandomException;
use Siteworxpro\App\Helpers\Rand;
use Siteworxpro\App\Models\ClientRedirectUri;
use Siteworxpro\App\Models\ClientScope;
use Siteworxpro\App\Models\ClientUser;
use Siteworxpro\App\Models\Model;
use Siteworxpro\App\Models\User;
use Siteworxpro\App\OAuth\AccessTokenRepository;
use Siteworxpro\App\OAuth\ClientRepository;
use Siteworxpro\App\OAuth\ScopeRepository;
/**
* Class Client
* @package Siteworxpro\App\Models
*
* @property string $id
* @property string $client_id
* @property string $client_secret
* @property string $name
* @property string $description
* @property string $private_key
* @property string $encryption_key
* @property Collection $grant_types
* @property bool $confidential
*
* @property ClientCapabilities $capabilities
* @property-read Collection<ClientRedirectUri> $clientRedirectUris
* @property-read Scope[]|Collection $scopes
*/
class Client extends Model implements ClientEntityInterface
{
use EntityTrait;
protected $casts = [
'id' => 'string',
'grant_types' => 'collection',
'confidential' => 'boolean',
];
/**
* @throws RandomException|EnvironmentIsBrokenException
*/
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->client_id = Rand::string(32);
$this->client_secret = Rand::string(64);
$this->generatePrivateKey();
}
public static function byClientId(string $clientId): ?Client
{
/** @var Client|null $client */
$client = self::where('client_id', $clientId)->first();
return $client;
}
/**
* @return void
* @throws EnvironmentIsBrokenException
*/
private function generatePrivateKey(): void
{
// generate rsa private and public key pair
$config = [
"digest_alg" => "sha256",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
];
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privateKey);
$this->private_key = $privateKey;
$this->encryption_key = Key::createNewRandomKey()->saveToAsciiSafeString();
}
/**
* @return HasMany
*/
public function clientRedirectUris(): HasMany
{
return $this->hasMany(ClientRedirectUri::class);
}
/**
* @return HasManyThrough
*/
public function scopes(): HasManyThrough
{
return $this->hasManyThrough(
Scope::class,
ClientScope::class,
'client_id',
'id',
'id',
'scope_id'
);
}
/**
* @return HasManyThrough
*/
public function users(): HasManyThrough
{
return $this->hasManyThrough(
User::class,
ClientUser::class,
'client_id',
'id',
'id',
'user_id'
);
}
/**
* @return string
*/
public function getIdentifier(): string
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return string|array
*/
public function getRedirectUri(): string|array
{
return $this->clientRedirectUris->pluck('redirect_uri')->toArray();
}
/**
* @return bool
*/
public function isConfidential(): bool
{
return $this->confidential;
}
public function getCapabilitiesAttribute(string $capabilities): ClientCapabilities
{
return ClientCapabilities::fromJson($capabilities);
}
/**
* @throws \JsonException
*/
public function setCapabilitiesAttribute(ClientCapabilities $capabilities): void
{
$this->attributes['capabilities'] = $capabilities->toJson();
}
/**
* @throws BadFormatException
* @throws EnvironmentIsBrokenException
* @throws \Exception
*/
public function getAuthorizationServer(): AuthorizationServer
{
$authorizationServer = new AuthorizationServer(
new ClientRepository($this),
new AccessTokenRepository(),
new ScopeRepository(),
$this->private_key,
Key::loadFromAsciiSafeString($this->encryption_key)
);
if (!empty($this->grant_types)) {
foreach ($this->grant_types as $grantType) {
switch ($grantType) {
case 'authorization_code':
$grant = new \League\OAuth2\Server\Grant\AuthCodeGrant(
new \Siteworxpro\App\OAuth\AuthCodeRepository(),
new \Siteworxpro\App\OAuth\RefreshTokenRepository(),
new \DateInterval('PT10M')
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M'));
break;
case 'client_credentials':
$grant = new \League\OAuth2\Server\Grant\ClientCredentialsGrant();
break;
case 'refresh_token':
$grant = new \League\OAuth2\Server\Grant\RefreshTokenGrant(
new \Siteworxpro\App\OAuth\RefreshTokenRepository()
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M'));
break;
default:
continue 2;
}
$authorizationServer->enableGrantType($grant);
}
}
return $authorizationServer;
}
public function loginUser(string $username, string $password): ?User
{
/** @var User|null $user */
$user = $this->users()->where('email', $username)->first();
if (!$user) {
return null;
}
return $user->verifyPassword($password) ? $user : null;
}
}