This commit is contained in:
gr
2026-09-24 09:45:44 +08:00
parent dbf474ddd7
commit bd22e5f476
38 changed files with 19152 additions and 0 deletions
@@ -0,0 +1,95 @@
<?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;
}
}