Files
zyt/server/app/common/service/iam/IamRevocationService.php
T

84 lines
3.0 KiB
PHP

<?php
declare(strict_types=1);
namespace app\common\service\iam;
use app\adminapi\logic\auth\AdminLogic;
use app\common\cache\AdminAuthCache;
use app\common\model\auth\Admin;
use app\common\model\auth\AdminSession;
use app\common\model\iam\IamRevocationEvent;
use think\facade\Db;
class IamRevocationService
{
private array $config;
public function __construct(?array $config = null)
{
$this->config = $config ?? (array) config('iam_hub');
}
public function apply(string $body, string $signature, string $eventHeader): void
{
if (!(bool) ($this->config['enabled'] ?? false)) {
throw new IamHubException('IAM Hub integration is disabled', 503);
}
$secret = (string) ($this->config['revocation_secret'] ?? '');
if (!IamSecurity::verifySignature($secret, $body, $signature)) {
throw new IamHubException('invalid IAM Hub signature', 401);
}
$event = json_decode($body, true);
if (!is_array($event)) {
throw new IamHubException('invalid IAM Hub event JSON');
}
$eventId = trim((string) ($event['id'] ?? ''));
$subject = trim((string) ($event['oidcSubject'] ?? ''));
$applications = $event['applicationIds'] ?? [];
if ($eventId === '' || $subject === '' || !is_array($applications)) {
throw new IamHubException('invalid IAM Hub event');
}
if (!hash_equals($eventId, trim($eventHeader))) {
throw new IamHubException('IAM Hub event ID header mismatch');
}
if (!in_array((string) ($this->config['application_id'] ?? 'zyt'), $applications, true)) {
throw new IamHubException('IAM Hub event is not addressed to this application', 403);
}
if (IamRevocationEvent::where('event_id', '=', $eventId)->find()) {
return;
}
Db::startTrans();
try {
$admin = Admin::where('iam_subject', '=', $subject)->find();
$status = 'admin_not_found';
$adminId = 0;
if ($admin) {
$adminId = (int) $admin->id;
$admin->disable = 1;
$admin->iam_revoked_at = time();
$admin->save();
$sessions = AdminSession::where('admin_id', '=', $adminId)->select()->toArray();
foreach ($sessions as $session) {
AdminLogic::expireToken((string) $session['token']);
}
(new AdminAuthCache($adminId))->clearAuthCache();
$status = 'applied';
}
IamRevocationEvent::create([
'event_id' => $eventId,
'iam_subject' => $subject,
'admin_id' => $adminId,
'status' => $status,
'reason' => (string) ($event['reason'] ?? ''),
'payload' => $event,
]);
Db::commit();
} catch (\Throwable $error) {
Db::rollback();
throw $error;
}
}
}