feat(kb-open): P0-A 认证体系 + 数据模型 + 管理端 + 限流 + 集中授权#444
Conversation
…authz Implements the authentication backbone for the KB Open API (mateaix#441): API key lifecycle, a permitAll-path filter that rejects (never pass-through), per-key sliding-window rate limiting, and a centralized @RequireKbScope interceptor for scope + KB-ownership checks. Components: - TokenHashUtil: shared SHA-256 hash kernel (A4), reusable by PAT later - KbApiKeyService: mint/authenticate/revoke/update + multi-KB binding (R3: empty binding = zero access, not "all KBs") - KbOpenApiAuthFilter: sole gatekeeper for /api/v1/open/kb/** (R1: must return 401, no pass-through); R2: per-key rate limit (429) - KbApiKeyRateLimiter: sliding-window limiter (TriggerRateLimiter pattern) - @RequireKbScope + KbScopeInterceptor: centralized authorization (A1), scope check + kbId ownership from path variable - KbApiKeyAdminController: JWT-authenticated CRUD (list/create/detail/ update/revoke), workspace-scoped - V162 migration (h2/mysql/kingbase): mate_kb_api_key + _binding tables Security: - mck_ prefix (distinct from PAT mc_ and JWT eyJ) - SHA-256 hash storage, plaintext shown once at creation - prefix column (4 chars) for UI display only Tests (17 new, all green): - KbApiKeyServiceTest: R3 empty-binding rejection, auth round-trip, expired/disabled/wrong-prefix rejection, kb:* wildcard, revoke - KbApiKeyRateLimiterTest: sliding window, per-key isolation, recovery Closes mateaix#441
|
感谢这份 P0-A 开放 API 基座 🙏 安全设计整体很扎实:token 只存 SHA-256( 不过有两个阻塞项需要先修,合并前请处理: 1. 2. 迁移版本号 V162 与已合并的 #437 冲突。 非阻塞建议:
P0-A 是整条 P0-B/Deep Research 栈的地基,建议先在这里把上面两个阻塞项(列宽 + 版本号)和设计文档位置改掉,#445/#446 再相应 rebase。改好后我们就合并 🙏 |
BLOCKERS: - prefix column VARCHAR(6) → VARCHAR(12) across all 3 migration dialects; KbApiKeyService.create() produces 8 chars (mck_ + 4 random), VARCHAR(6) would silently truncate on H2 and throw on MySQL strict mode - Rename migration V162 → V164 to avoid collision with merged mateaix#437 (V162=wiki_raw_material_error_code, V163=wiki_raw_material_warning) and fix stale V161 references in h2/kingbase comments NITS: - SecurityConfig/WebMvcConfig: replace inline FQN with import + simple name - parseScopes: add .map(String::trim) so ' kb:read' matches correctly - Remove ?token= SSE query fallback in KbOpenApiAuthFilter (P0-A has no SSE endpoint; key would leak into access/proxy logs — R5) - Move kb-open-api-design.md from repo root to rfcs/ (contains RFC-090 internal reference that would be exposed by sync-opensource) - KbApiKeyEntity Javadoc: 'first 4 chars' → 'first 8 chars (mck_ + 4)' to match actual behavior
|
Fixed. Commit Blockers:
Nits: Compilation and all 17 KB Open API tests pass. Ready for re-review. |
|
感谢按 review 全部改好了 🙏 两个阻塞项都已解决:
其余建议也都采纳了: 一个可选小尾巴(不阻塞): |
Per mateaix#444 review (4825157096): parseScopes used `.collect(java.util.stream.Collectors.toUnmodifiableSet())` while `Collectors` is already imported at the top of the file. Use the simple name. Zero behavior change.
* feat(kb-open): P0-B 9 open API endpoints Implements the 9 read-only KB Open API endpoints on top of the P0-A auth skeleton (#441). Each returns an explicit DTO (A5: never raw entities) and delegates assembly to service-layer methods that return pure DTOs (A6: no HTTP coupling, MCP-ready). Endpoints: - GET /pages/{slug} entity card (mode=summary/full/section:{heading}) - POST /search hybrid retrieval (granularity=entity/chunk) - POST /search/chunks chunk-level semantic search - POST /pages/{slug}/traverse entity relation graph (depth ≤ 2) - GET /pages/{slug}/trace provenance (page → chunk → raw) - GET /taxonomy pageType/entityType/relationType enumeration - GET /whats-new recent changes + stale pages - GET /stats KB statistics - GET /pages lightweight page list Components: - KbOpenApiController: 9 endpoints, each @RequireKbScope annotated - KbOpenApiService: assembly layer (card, traverse BFS, metadata parsing) - KbOpenApiDtos: all response DTOs as records (PageCard, TraceResult, TaxonomyResult, KbStats, WhatsNewResult, TraverseResult, PageList) Traverse (pragmatic version): - depth ≤ 2 with explosion guard, predicate LIKE matching - slug → pageId → mention → primaryEntity (salience-highest) - neighbor nodes echo slug when available (R11) - edge sourceHandle via evidenceChunkId → citing page Tests (4 new, all green): - KbOpenApiControllerTest: 404 on missing page/slug, delegation to service Closes #442 * fix(kb-open): address review feedback on #445 BLOCKERS: - stats.pagesWithLinks always returned 0 because listByKbId() nulls out content. Switch to listByKbIdWithContent() so [[wiki link]] detection works. - Test file: replace inline java.util.List.of() FQN with import + simple name (sync-opensource would expose the unidiomatic style). NITS (inherited from P0-A rebase): - V162→V164, prefix VARCHAR(12), FQN imports, parseScopes trim, ?token= fallback removal, design doc moved to rfcs/ — all now in ancestor commit 6fd6244. EXTRA: - whatsNew staleReason: hardcoded Chinese "上游 fact 页面变更" → English "Upstream fact page changed" (external-facing API response). * chore(wiki): drop RFC-012 prefix from progress field Javadocs (#449 nit) Per #449 review (4825113234): the internal RFC-012 reference should not appear in code. progressPhase/progressTotal/progressDone Javadocs still carried the "RFC-012 M2 v2 UI:" prefix after #449's English translation pass — drop it now that these lines are touched. Zero behavior change. * chore(kb-open): drop inline FQN in parseScopes (#444 nit) Per #444 review (4825157096): parseScopes used `.collect(java.util.stream.Collectors.toUnmodifiableSet())` while `Collectors` is already imported at the top of the file. Use the simple name. Zero behavior change.
Closes #441 · Part of #440
改动
知识库开放 API 的认证骨架。这是 P0-B(9 个端点,#442)的前置依赖。
认证与鉴权
TokenHashUtil(共享 hash 内核,A4):SHA-256 生成/校验,KB-key 与 PAT 共享(先 hash 层,CRUD/UI 后续)KbApiKeyService:签发(mck_前缀)/ 校验(hash 索引 O(1))/ 撤销 / 多 KB 绑定管理KbOpenApiAuthFilter:permitAll 路径的唯一守门人@RequireKbScope+KbScopeInterceptor(A1):集中授权(scope 检查 + KB 归属校验),仿@RequireWorkspaceRole,不逐端点手写KbApiKeyRateLimiter:滑动窗口限流器(复用TriggerRateLimiter范式)管理端
KbApiKeyAdminController:JWT 认证 CRUD(list/create/detail/update/revoke),workspace 隔离,@RequireWorkspaceRole("admin")数据模型
mate_kb_api_key+mate_kb_api_key_bindingtoken_hashSHA-256(UNIQUE 自带索引)、prefix(4 位)、rate_limit_per_min(P0 强制)kb_id索引(Wiki 面板反查)SecurityConfig
/api/v1/open/kb/**permitAll + KbOpenApiAuthFilter 注册WebMvcConfig.addCorsMappings("/api/**")覆盖(R6)测试
回归验证:
WebChatApprovalInteractionTest(9)全绿。安全设计要点