From 5261b8a4420e9d3265f65ccbd9464563b2a4f346 Mon Sep 17 00:00:00 2001 From: Dmitry Maranik Date: Tue, 16 Jun 2026 22:11:58 -0700 Subject: [PATCH] fix(knowledge): scope keyword detail/delete to the caller's tenant GET /v1/knowledge/keywords/detail and DELETE /v1/knowledge/keywords were not scoped to the caller's org/user, so any authenticated user could read or permanently delete another organization's KB keyword by its enumerable integer id. The sibling list/repeat-check helpers already scope via WithPermit(org,user) and the other /knowledge/* routes carry AuthKnowledge; these two were the exception. - GetKnowledgeKeywordsDetail: verify the loaded keyword belongs to the caller (req.Identity) before returning. - bff DeleteDocCategoryKeywords: gate the delete on the now tenant-scoped detail lookup (the delete RPC carries no Identity). --- internal/bff-service/service/knowledge_keywords.go | 11 +++++++++++ .../grpc/knowledge_keywords/knowledge_keywords.go | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/internal/bff-service/service/knowledge_keywords.go b/internal/bff-service/service/knowledge_keywords.go index a6e8c5de3..391eb3216 100644 --- a/internal/bff-service/service/knowledge_keywords.go +++ b/internal/bff-service/service/knowledge_keywords.go @@ -97,6 +97,17 @@ func UpdateKnowledgeKeywords(ctx *gin.Context, userId, orgId string, r *request. } func DeleteDocCategoryKeywords(ctx *gin.Context, userId, orgId string, r *request.DeleteKeywordsReq) error { + // 删除前校验关键词归属:detail 接口已按 (org,user) 鉴权,非本租户的关键词会返回错误, + // 以此拦截跨租户删除(DeleteKnowledgeKeywords 请求自身不携带调用方身份)。 + if _, err := knowledgeBaseKeywords.GetKnowledgeKeywordsDetail(ctx.Request.Context(), &knowledgebase_keywords_service.GetKnowledgeKeywordsDetailReq{ + Id: r.Id, + Identity: &knowledgebase_keywords_service.Identity{ + UserId: userId, + OrgId: orgId, + }, + }); err != nil { + return err + } _, err := knowledgeBaseKeywords.DeleteKnowledgeKeywords(ctx.Request.Context(), &knowledgebase_keywords_service.DeleteKnowledgeKeywordsReq{ Id: r.Id, }) diff --git a/internal/knowledge-service/server/grpc/knowledge_keywords/knowledge_keywords.go b/internal/knowledge-service/server/grpc/knowledge_keywords/knowledge_keywords.go index 8a06d2e34..6d15f963e 100644 --- a/internal/knowledge-service/server/grpc/knowledge_keywords/knowledge_keywords.go +++ b/internal/knowledge-service/server/grpc/knowledge_keywords/knowledge_keywords.go @@ -110,6 +110,13 @@ func (s *Service) GetKnowledgeKeywordsDetail(ctx context.Context, req *knowledge log.Errorf(fmt.Sprintf("GetKnowledgeKeywords 失败(%v) 参数(%v)", err, req)) return nil, util.ErrCode(errs.Code_KnowledgeKeywordsInfoFailed) } + // 校验关键词归属:仅允许访问本租户(org+user)的关键词,避免按 id 跨租户越权读取。 + // 列表/重复校验接口已通过 WithPermit(org,user) 做此限制,此 detail 接口此前遗漏。 + if keywords.OrgId != req.Identity.OrgId || keywords.UserId != req.Identity.UserId { + log.Errorf(fmt.Sprintf("GetKnowledgeKeywordsDetail 跨租户越权: keyword(org=%v,user=%v) caller(org=%v,user=%v)", + keywords.OrgId, keywords.UserId, req.Identity.OrgId, req.Identity.UserId)) + return nil, util.ErrCode(errs.Code_KnowledgeKeywordsInfoFailed) + } // 构造返回体 keywordsInfo, err := buildKeywordsInfo(ctx, keywords) if err != nil {