From e95e618372d868e8f151292d85a9a5147bb0ea51 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 2 Sep 2026 16:14:18 +0800 Subject: [PATCH] =?UTF-8?q?security:=20=E6=9D=83=E9=99=90=E9=97=A8?= =?UTF-8?q?=E7=A6=81=E6=94=B6=E7=B4=A7=E4=B8=BAowner=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _require_admin→_require_owner: 持有任一orgtypeid='owner'角色(含通配)放行 - 不再仅限superuser/admin; 其他组织(含ownerx前缀伪造)一律拒绝 - 工具描述/文案同步更新 --- .../platform_ability.cpython-310.pyc | Bin 19073 -> 19467 bytes pipeline_platform/platform_ability.py | 53 +++++++++--------- wwwroot/agent_platform/index.ui | 2 +- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/pipeline_platform/__pycache__/platform_ability.cpython-310.pyc b/pipeline_platform/__pycache__/platform_ability.cpython-310.pyc index b35b026367676933f8e08d2f46f2331454e0afed..2bbe52a17911610ddf161cb82210d2e6cf2516e6 100644 GIT binary patch delta 3922 zcmaJ^dvH|M8Q*hv_inOzup|VCQCJ`l*O(=tiSmdEO9%voT-fZkm9-Xs_A?!BCQ zm&6z*;UR*`LyicgV1afJ>tNL#9cRFQI{m{=ooStMrZdO(4{5@~PQ{LW+E)90=R%r< zjQjZQJ>U18?>pyvowK`+krz&oLaU%4?1NW5KfdkqmbVKtg#0VJmz*c-uD-bI>Zt=! zt1qq7=(S6GuU&fX+R44wUpspJz&kfD@3&}=`?Rea+CvpWq~hk~7e|l0abx)8jb{%= zJzD%epdMgb{C8JQt>?<{%kUX}_PvoyuZ)?<9`whh5DyBq?9<@)$YnN7ZXW9K?FnZC1x z__kNL0WD9<-|HI;toH38dkAgUqz<1};09gU4Gjj6W^dLCaYlzPHZAj!k9{nclzoR! z*k9zzp>Q|~uhIQK9ewlU&C4eR0w1#h>WIZR=X2qel**uP&Y#J#}H^(xs92 zhog5mGdkYYQ@iz#!@Gqs9zrXMp>bb^Y+|ucQ}G06dl6%q&j!M~z(H@*Ae!-m zfvd;Q#B#X5M|ZjGY&VbjK6s%`6Ojr-N~)3gYLLByi24irasaT*M{Kk4>! ze+Hb)F6NYUifwr?6Q>kTT>O1Na}cEANH9R~EfJZ7zbWzuNIr?cJBJhzX}CymB1Ha{ zRVVC7@8NZxt{c z86Ygp0CTh^dpsN=q=j9`yDxuyb+b?N9Fou4^4phTOW-6aVjPp`T{!d`Y4>$WEO07g zvgGDdu1o8=3Y%MFonK0`R~# z$4c{{mPy%8ESJtjnry37O*u5m5|QGv`9e%hCLN029lu^?Masw{?1e}pd6fM+@^=zu zznC8Pe@zy%zfJ#L7oCn%gQnG|Q=S8P=kytFFSvNUP}yra?s#oToUZIpO+!;%UD2$h zlA&fA$W8iL6DAqmkc}7D60n(^v2oM)L4tOmiXdn;Y_PHsM}D{e4kHCh zubVOz*HT=)-yHxkX9hr#R7fVia|l(lGfEzoWLz-~Gi}zpWFxz4_M;Ku!P!Vv5H_$K zvnz*$`*j%e0BYvB&HYK;b`9{bA67*>ktLgAffP%Isz~vhdK#)~ePW}sx~)lRYDu(r z#0o_$=vtJDBD5fQp4$P9mZos50q}sMI}Q)f6}n^dU=mL%?x$)WCpxuJYEp_7DZNU! z?k0Pcl%?q=dU`+GT-rtkvu~6_v4+{jd2fQW*8n+|H>F>8qEa+UF`RV8Gl*RP;2c{|a*~?tgjwJO zh!Itju40-gMYRnDMSsbDv!H_8F`8{>_qnePWcdHrmj$D$I?rXqzumeaW zESjD4Ej^a~sJuxghghVt8vL24TvK=g)yP!ufpnl6J62gEhk!&^v5zXBFJ6R?`6wb8 z+JLlqD{W=Q{i~aBmS``u6`mlgG3P+d?Igz*O2uv`23Zt)DI8lg&c3|AN@)14#~F;KLK{GNOkxeu_K>NZlyc2&=fOtRU?-l~33u0yE?_UGz3 z5wt^JLs*HBU}ZIijA=j*v*&A0*Pn;Sn9L>HfUm`+B>W~VPBeNBg=ztK?ha^pmbj{f zp?O-Fv{ zUqumY-riXf!1KZjOc@f#q9f{sx~PUU(R+|L2M0yUtz3romty&~r3p9%EF&E&pR%+L z;lx)EFg8>~NgM=X9e6s>_nBTBB|l;NYE5#OeO0?MZ(J-`L)~ohMRr5oBP8&Ux-FeXJmXL-x#LitrLuj$;4tS

r!x9ibRDm+$llJpbs_5t0JKqk zJMG8exEovB*CUo@phs<;#tB?euMBW`){I(46utUDp>of`tQ)4 zIm*thdT8c^{{UnECbD0wD)LYD{~(*YCQ3^FgQnoJ54U>+2_e85*1ns-Wo+H@W~s%;sGNGjmbNU@wAPFc;0PcugGg#%EA&4_nbdDMvK8lb* z*nprS3?l4DID&8t;WGdz9k^NZpqsHxU8FI^bfJZ4VH!dN0T=lHj0<^>GufZkRg&3k I*2CNW2g8SM=l}o! delta 3430 zcmZuz32YSC8QwRuJA2v2H((RI1{S=*HpUKzjRBtpL%_&RP{X0|vNN^^W_N}+GuUx# zYyzR2;doF8kQS#>OQIHY_W@BaME_7j`m&aeo1Ke>;5oy2aP+ZSqUsBaA2{>I7M z2fq{g=)i%{OIlAtrsamE%hWPtS{h0TqEcK*417HFav&5M+5gI&y_Z7KE;(U_MvlET zdg18Eq2XJXhi@IPxeO{NV$+fj;z+>|L9?2(hdwG4K#0?0}-0#e*Veai0=&F7%{JJl?r9i*X z@9pIBUBiU9c9!(JP0sY}C3P;kLE_q7Qcy~}D43pMVtV`Cdx^eDN=NN>S0uw~fgsHs zy*Pa5ts^5h*~r18w@zIeJ@CEJvscEi#{QH&EB})p4v+4+0->GwX7H0TAyuhx82({B>~zRHDw8nVPx|$v)ATX zabUXpO&2X)JjUsjA?OwMFTq+tXMh=r5?qU<3@zg z8ROcHonzt_sn)X+77PI(z~#bjv^4o?Fq4pG#-%?!A+k%;4H9JEOmD09gRV_tl)&Ez zN3(ItD+%*Wj)r0Uq@8F9EVQvD86R#*M-|6~cUx4ACStPVh*p3+!c%AY8Pq`mxOCim zI#n%d(khfcc5PKLQrF&?)TgW>LTU$gGqb+YaVn&oR-=;4w1n-EthjDO{B$bH1U*Wlaf4372`^J90nIXP-rT0r0>>yCiy3q| z3@4tRy`NP`Rmp!sz^~&79}kvT2$S^;=rv^GuAD}HAz#iWmEbMgjN(4cVy z=SJ&cWDC^E9Ie9%q%SHN6k`rlAxCMf+a~R{r;BQqPU}G&2|9%|IRw41MEejdgk1=m z0c^LS%Q2h8F%rH99KeF%8RSQ-B-~Dpu>SDIm5Wi;|J;kZ+)OF(Qeb0LwNhiq_dHzn zN^)n^QXk7XCw6|qvgYQH6Up%0Mu8k+N@)cI@?7Z`GESkPK;V|H0-BN{v1?}*_TSS%Q>u_|;3#JT9BNp^ZMpMY_M zwUn0$XVH(y`pdWaoDA`F7yEtrg~3PtQ%CKzql@rmgfjpUx3e+5fPzYd6DUm)n9a%h zq#AR!p9R|+X`m;#I4=8M8W42g9xVfb?Z;M2XeRr7L9l!fU3uww_>*Fu*Kov8FDS`I z4EoK)*g1W%hb^jzF0H{u-mEm&15;y2#*RV{qv@leu>H000+4%kx}u!DS8+{v9d#D6 zeG6MFkoutBqT6KE7A(bt+}pmfFAsd5CZDN>@s&$b{aMZGDraWmIq26AQXI(=m5P9s zLJzYKD_R4ni zESgbA^FSDx^TeJyghnS3Fd@`QjYG`NF$z$2m0epDB41@6EKo z!66hOb+gpDQ2ILnn}<@M22Lm32&eA=n2gkJChsJ-)-sOFW}mJo8gyFA>yV!v5Je@f z!$U`erwrOSS^X)F>JXf5eNT$H?FL>pQmybeRQ)@`qbIO)fW&n*YIbTgjy!IuvO&(X z<1533{{nS8XM9mr3Y(Ts8To#4c2HMzSyd7;e$Ud`4_fAsLiTZs`bh0=S~HXUlfAemJbwys8Bonp z@D?eyy`uA05S>igURCSrQW9Ox7mNOt{cO$L!2syfTs+LX2yujBgi?f62s%O=!WM)c zgnbA{5RhLfHVhOyJX39tY3Zu$6duI{(;)7UhLDbc2Ygr~-qbL}thMt=9vfb}{r^?f B%whll diff --git a/pipeline_platform/platform_ability.py b/pipeline_platform/platform_ability.py index 432509d..7d6c7ae 100644 --- a/pipeline_platform/platform_ability.py +++ b/pipeline_platform/platform_ability.py @@ -5,8 +5,8 @@ pipeline-service: platform_ability — 平台内部 agent 能力包(pipeline_i 核心能力是「通读模型 API 文档 → 自动生成模型治理配置(供应商/适配模板/模型/定价)」。 权限模型(代码层硬门禁,不依赖 prompt): - 所有工具 handler 入口校验调用者的 RBAC 角色 ∈ {owner.superuser, owner.admin}。 - 内部 agent 只服务管理员——普通用户即使打开页面也调不动任何工具。 + 所有工具 handler 入口校验调用者持有 owner 组织的角色(orgtypeid='owner',含通配 'owner.*')。 + 内部 agent 只服务 owner 组织的角色——其他组织的用户即使打开页面也调不动任何工具。 配置生成链路(用户给定 API 文档 → 完成配置): ① fetch_model_doc:抓取文档页面(SSRF 防护:仅 http(s) 公网域名) @@ -29,8 +29,8 @@ logger = logging.getLogger("pipeline.platform_ability") PLATFORM_PIPELINE_ID = "platform_general" -# 管理角色:内部 agent 仅管理员可用 -ADMIN_ROLES = ("owner.superuser", "owner.admin") +# 组织门禁:内部 agent 仅 owner 组织的角色可操作(任一 owner.* 角色,含通配) +OWNER_ORG = "owner" # 当前运行时调用链支持的协议(llm_bridge 固定 OpenAI 兼容路径)。 # 其他协议的适配模板会存入 llm_api_profile 备查,但运行时暂不渲染。 @@ -39,25 +39,28 @@ RUNTIME_PROTOCOLS = ("openai_compat",) # ────────────────────── 权限门禁(代码层) ────────────────────── -async def _require_admin(sor, ctx) -> str: - """校验当前用户是管理员。返回 ''=通过,否则错误信息。""" +async def _require_owner(sor, ctx) -> str: + """校验当前用户持有 owner 组织的角色。返回 ''=通过,否则错误信息。 + + 判定:userrole→role JOIN 后,存在任一 orgtypeid='owner' 的角色(含通配 'owner.*')。 + """ uid = ctx.get("user_id", "") or "" if not uid: - return "无法识别当前用户身份(未登录),内部 agent 仅管理员可用" + return "无法识别当前用户身份(未登录),内部 agent 仅 owner 组织角色可用" recs = await sor.sqlExe( "SELECT r.orgtypeid, r.name FROM userrole ur JOIN role r ON ur.roleid=r.id " "WHERE ur.userid=${u}$", {"u": uid}) await sor.sqlExe("COMMIT", {}) - roles = set() + roles = [] for r in (recs or []): o = getattr(r, "orgtypeid", "") or "" n = getattr(r, "name", "") or "" if o and n: - roles.add("%s.%s" % (o, n)) - if roles & set(ADMIN_ROLES): + roles.append("%s.%s" % (o, n)) + if any(fn.split(".", 1)[0] == OWNER_ORG for fn in roles): return "" - return "权限不足:内部 agent 工具仅管理员(%s)可用,当前角色 %s" % ( - "/".join(ADMIN_ROLES), sorted(roles) or "无") + return "权限不足:内部 agent 工具仅 %s 组织的角色可操作,当前角色 %s" % ( + OWNER_ORG, sorted(set(roles)) or "无") def _row(r): @@ -70,8 +73,8 @@ def _row(r): # ────────────────────── 工具 1:模型治理状态 ────────────────────── async def _h_platform_llm_status(sor, params, ctx): - """供应商/账号/模型/用量概览(管理员诊断配置用)。""" - err = await _require_admin(sor, ctx) + """供应商/账号/模型/用量概览(owner 组织诊断配置用)。""" + err = await _require_owner(sor, ctx) if err: return err out = {} @@ -132,7 +135,7 @@ def _html_to_text(html: str) -> str: async def _h_fetch_model_doc(sor, params, ctx): """抓取模型 API 文档页面 → 纯文本(供 LLM 提取配置规格)。""" - err = await _require_admin(sor, ctx) + err = await _require_owner(sor, ctx) if err: return err url = (params.get("url") or "").strip() @@ -192,7 +195,7 @@ _EXTRACT_PROMPT = """你是大模型 API 配置专家。通读下面这份模型 async def _h_extract_llm_api_spec(sor, params, ctx): """LLM 通读文档文本 → 结构化配置规格(JSON)。""" - err = await _require_admin(sor, ctx) + err = await _require_owner(sor, ctx) if err: return err doc_text = (params.get("doc_text") or "").strip() @@ -238,7 +241,7 @@ async def _h_apply_llm_config(sor, params, ctx): 幂等:供应商按名称复用;模型按 (vendor, vendor_model_id) 复用—— 已存在则更新定价,不重复创建。 """ - err = await _require_admin(sor, ctx) + err = await _require_owner(sor, ctx) if err: return err try: @@ -410,7 +413,7 @@ def _default_templates(protocol: str, capability: str, spec: dict) -> tuple: async def _h_platform_modules(sor, params, ctx): """列出平台已装载的业务模块(内部 agent 了解平台构成用)。""" - err = await _require_admin(sor, ctx) + err = await _require_owner(sor, ctx) if err: return err mods = [] @@ -439,39 +442,39 @@ async def _h_platform_modules(sor, params, ctx): PLATFORM_TOOLS = [ ToolDefinition( name="platform_llm_status", - description="查看模型治理状态:供应商/账号余额/模型定价/各状态分布。用户问「模型配置状态/账号余额」时调用。仅管理员可用。", + description="查看模型治理状态:供应商/账号余额/模型定价/各状态分布。用户问「模型配置状态/账号余额」时调用。仅owner组织角色可用。", parameters={}, category="platform", ), ToolDefinition( name="fetch_model_doc", - description="抓取大模型供应商的 API 文档页面(返回纯文本)。配置新模型前先用此抓取官方文档。仅管理员可用。", + description="抓取大模型供应商的 API 文档页面(返回纯文本)。配置新模型前先用此抓取官方文档。仅owner组织角色可用。", parameters={"url": "文档页面 URL(必须是公网 http/https)"}, category="platform", ), ToolDefinition( name="extract_llm_api_spec", - description="通读文档文本,LLM 提取 API 配置规格(端点/协议/请求响应格式/定价)。配合 fetch_model_doc 使用。仅管理员可用。", + description="通读文档文本,LLM 提取 API 配置规格(端点/协议/请求响应格式/定价)。配合 fetch_model_doc 使用。仅owner组织角色可用。", parameters={"doc_text": "fetch_model_doc 返回的文档文本"}, category="platform", ), ToolDefinition( name="apply_llm_config", - description="按提取的规格写入模型治理配置:供应商/端点/适配模板/模型(含定价)。幂等——已有模型只更新定价。仅管理员可用。", + description="按提取的规格写入模型治理配置:供应商/端点/适配模板/模型(含定价)。幂等——已有模型只更新定价。仅owner组织角色可用。", parameters={"spec": "extract_llm_api_spec 返回的 JSON 规格"}, category="platform", requires_confirmation=True, ), ToolDefinition( name="platform_modules", - description="列出平台已装载的业务模块清单。用户问「平台有哪些模块」时调用。仅管理员可用。", + description="列出平台已装载的业务模块清单。用户问「平台有哪些模块」时调用。仅owner组织角色可用。", parameters={}, category="platform", ), ] PLATFORM_PROMPT = """ -你是产线平台的内部运维 agent,服务对象是平台管理员。 +你是产线平台的内部运维 agent,服务对象是 owner 组织的角色。 ## 模型自动配置工作流(用户给你文档 URL 要求配置模型时) 1. `fetch_model_doc` 抓取官方文档页面 @@ -484,7 +487,7 @@ PLATFORM_PROMPT = """ - cost_*(供应商成本)未知时与 price_* 相同 - 非 openai_compat 协议的模型:模板会存档但运行时暂不可调用,必须如实告知 - 文档抓取失败/内容不足时如实说明,不要凭记忆编造 API 格式 -- 所有工具仅管理员可用,权限报错时如实转告用户 +- 所有工具仅 owner 组织角色可用,权限报错时如实转告用户 ## 平台知识 平台模块清单用 `platform_modules` 查询;技能库中「平台」相关技能(skills_library/all/ 与 diff --git a/wwwroot/agent_platform/index.ui b/wwwroot/agent_platform/index.ui index fb837d5..0e0be93 100644 --- a/wwwroot/agent_platform/index.ui +++ b/wwwroot/agent_platform/index.ui @@ -7,7 +7,7 @@ "options": {"width": "100%", "alignItems": "center", "padding": "16px 24px 8px 24px", "cheight": 6, "gap": "12px"}, "subwidgets": [ {"widgettype": "Title2", "options": {"text": "平台内部助手"}}, - {"widgettype": "Text", "options": {"text": "平台运维/管理 agent · 模型自动配置 · 仅管理员可用", "cfontsize": 0.9, "color": "#94a3b8"}}, + {"widgettype": "Text", "options": {"text": "平台运维/管理 agent · 模型自动配置 · 仅owner组织角色可用", "cfontsize": 0.9, "color": "#94a3b8"}}, {"widgettype": "Filler"}, { "widgettype": "Button",