feat(mcp): 密钥授权页,仅创建者拥有的节点可授权(防越权与漂移)

This commit is contained in:
augushong
2026-08-16 21:18:40 +08:00
parent 25c98b6ed6
commit 744a0819f2
7 changed files with 295 additions and 7 deletions

View File

@@ -96,6 +96,33 @@ class AuthServiceBase
return static::$dynamicNodeList[$node];
}
// 判断是否需要获取当前节点
if (empty($node)) {
$node = $this->getCurrentNode();
} else {
$node = $this->parseNodeStr($node);
}
return $this->checkNodeResolved($node);
}
/**
* 对已规范化parseNodeStr 后)的单个节点执行完整判定链.
*
* checkNode 与 getAdminAllowedNodes 共用此实现,严禁平行复制导致语义漂移。
* 判定链(顺序与原 checkNode 保持一致):
* 动态节点黑名单 → 超管直通 → auth_on 开关 → 未注册节点 default_auth_check
* → 注解 auth=false → status/auth_ids 校验 → 白名单成员判定
* @param string $node
* @return bool
*/
protected function checkNodeResolved($node)
{
// 动态节点开关(按规范化节点命中)
if (isset(static::$dynamicNodeList[$node])) {
return static::$dynamicNodeList[$node];
}
// 判断是否为超级管理员
if ($this->isSuperAdmin()) {
return true;
@@ -104,12 +131,6 @@ class AuthServiceBase
if ($this->config['auth_on'] == false) {
return true;
}
// 判断是否需要获取当前节点
if (empty($node)) {
$node = $this->getCurrentNode();
} else {
$node = $this->parseNodeStr($node);
}
// 判断是否加入节点控制,优先获取缓存信息
if (!isset($this->nodeList[$node])) {
@@ -131,6 +152,35 @@ class AuthServiceBase
return false;
}
/**
* 批量获取管理员对候选节点集合的允许子集.
*
* 对每个候选节点走与 checkNode() 完全一致的判定链(共用 checkNodeResolved
* 返回其中允许访问的节点(规范化后的形式,保持候选顺序并去重)。
* 用于 MCP 密钥授权(创建者权限子集)等批量场景。
* @param array $candidateNodes 候选节点列表controller/action 形式,内部会做 parseNodeStr 规范化)
* @param int|null $adminId 管理员ID为 null 时沿用当前实例的管理员
* @return array 允许的节点列表
*/
public function getAdminAllowedNodes(array $candidateNodes, ?int $adminId = null): array
{
if (empty($adminId)) {
$adminId = $this->adminId;
}
// 目标管理员与当前实例不一致时,按目标管理员重建判定上下文
$service = ($adminId == $this->adminId) ? $this : new static($adminId);
$allowedNodes = [];
foreach ($candidateNodes as $node) {
$node = $service->parseNodeStr($node);
if ($service->checkNodeResolved($node) && !in_array($node, $allowedNodes)) {
$allowedNodes[] = $node;
}
}
return $allowedNodes;
}
/**
* 获取当前节点.
* @return string