Files
zyt/server/app/mcp/controller/AuthController.php
T
2026-09-24 09:45:44 +08:00

96 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\mcp\controller;
use app\BaseController;
use app\mcp\service\AuditLogger;
use app\mcp\service\Catalog;
use app\mcp\service\GrantService;
use app\mcp\service\Guard;
use app\mcp\service\McpConfig;
use app\mcp\service\McpException;
use think\Response;
/**
* AI 授权接口(供行知等客户端调用):
* POST /mcp/auth/grant 账号 + 密码 → 只读令牌(密码只用于本次校验,不保存)
* POST /mcp/auth/revoke 撤销当前令牌(Bearer)
* GET /mcp/auth/whoami 当前令牌对应的账号(Bearer)
*/
class AuthController extends BaseController
{
public function grant(): Response
{
$blocked = $this->blocked('POST');
if ($blocked) {
return $blocked;
}
$input = json_decode((string) $this->request->getInput(), true);
if (!is_array($input)) {
$input = $this->request->post();
}
$ip = $this->request->ip();
try {
$data = GrantService::issue($input, $ip);
AuditLogger::log(['grant_id' => $data['grant_id'], 'admin_id' => $data['admin']['id'], 'tool' => 'auth.grant',
'arguments' => ['client' => $input['client'] ?? '', 'client_instance' => $input['client_instance'] ?? ''], 'status' => 'ok', 'ip' => $ip]);
return Guard::envelope(1, '授权成功', $data);
} catch (McpException $e) {
AuditLogger::log(['tool' => 'auth.grant', 'arguments' => ['account' => (string) ($input['account'] ?? '')], 'status' => 'denied',
'message' => $e->reason, 'ip' => $ip]);
return Guard::envelope(0, $e->getMessage(), ['reason' => $e->reason], $e->httpStatus === 401 ? 200 : $e->httpStatus, 1);
}
}
public function revoke(): Response
{
$blocked = $this->blocked('POST');
if ($blocked) {
return $blocked;
}
try {
$identity = GrantService::authenticate($this->request);
} catch (McpException $e) {
return Guard::envelope(-1, $e->getMessage(), ['reason' => $e->reason], 401);
}
GrantService::close((int) $identity->grant['id'], GrantService::STATUS_REVOKED, 'client_revoke');
AuditLogger::log(['grant_id' => $identity->grant['id'], 'admin_id' => $identity->adminId, 'tool' => 'auth.revoke', 'status' => 'ok', 'ip' => $this->request->ip()]);
return Guard::envelope(1, '已撤销');
}
public function whoami(): Response
{
$blocked = $this->blocked('GET');
if ($blocked) {
return $blocked;
}
try {
$identity = GrantService::authenticate($this->request);
} catch (McpException $e) {
return Guard::envelope(-1, $e->getMessage(), ['reason' => $e->reason], 401);
}
return Guard::envelope(1, '', [
'admin' => $identity->publicProfile(),
'grant' => GrantService::publicGrant($identity->grant),
'data_scope' => $identity->dataScopeText(),
'resources' => ['open' => count(Catalog::openFor($identity))],
]);
}
private function blocked(string $method): ?Response
{
if (!McpConfig::enabled()) {
return Guard::envelope(0, 'AI 助手接口未启用', ['reason' => 'feature_disabled'], 503, 1);
}
if ($this->request->method(true) !== $method) {
return response('', 405)->header(['Allow' => $method]);
}
$guard = Guard::check($this->request);
if ($guard !== null) {
return Guard::envelope(0, $guard[1], ['reason' => $guard[2]], 200, 1);
}
return null;
}
}