feat(security): ADR-0090 D5/D9 suggested-audience-binding surface + confirm/dismiss API - #2746
Merged
Conversation
…m/dismiss API (ADR-0090 D5/D9) A package permission set declaring isDefault: true is an install-time SUGGESTION to bind the set to the builtin everyone position — never auto-bound. Until now the flag was only read at bootstrap as the fallback-set name; after an install there was no way to see or act on the suggestion (framework#2696 remaining item). plugin-security: - new sys_audience_binding_suggestion system object (read-only data API, unique per package × set × anchor) - convergent reconciler syncAudienceBindingSuggestions reading declared stack metadata AND installed package manifests (a runtime POST /api/v1/packages install is visible immediately); prunes pending rows on uninstall, marks out-of-band bindings confirmed (observed); runs at boot, after a permission publish, and on every list call - security service gains listAudienceBindingSuggestions / confirmAudienceBindingSuggestion / dismissAudienceBindingSuggestion, all pre-gated on tenant-level admin (anchors stay tenant-level, D12); confirm inserts sys_position_permission_set WITH the caller's execution context so the D5/D9 anchor gate and D12 delegated-admin gate enforce the write; not-yet-materialized sets are seeded through the provenance-checked upsert (ADR-0086 D4) runtime: - GET /api/v1/security/suggested-bindings?status=&packageId= - POST /api/v1/security/suggested-bindings/:id/confirm - POST /api/v1/security/suggested-bindings/:id/dismiss (401 unauthenticated; 403/404/409 from typed service errors; 503 without plugin-security) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 3 package(s): 25 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…DR-0090 D5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
… polynomial-redos)
split('/')+filter(Boolean) already discards empty segments; the
^\/+|\/+$ regex over request-controlled input was flagged by CodeQL
as polynomial backtracking.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
…ace (ADR-0090 D5/D9) The hono dev/production stack serves /api/v1 through @objectstack/rest, not the runtime HttpDispatcher, so the suggested-bindings routes must be registered there too (same pattern as sharing rules: a security service provider injected by rest-api-plugin, resolveExecCtx + enforceAuth per request, 501 when plugin-security is absent, typed 403/404/409 mapping). Verified end-to-end against a fresh showcase run: anonymous list → 401; admin list → the showcase's second isDefault set (showcase_member_default) appears as a pending suggestion; confirm → 200 with the binding row created by the caller (not system) in sys_position_permission_set; re-confirm → 409. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
os-zhuang
marked this pull request as ready for review
July 10, 2026 08:02
os-zhuang
pushed a commit
that referenced
this pull request
Jul 10, 2026
Both sides added a securityServiceProvider (mine for /security/explain, main's for /security/suggested-bindings): kept one shared provider, kept main's registerSecurityEndpoints (suggested-bindings) and renamed the explain registrar to registerSecurityExplainEndpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTubNprerCmJoU1VtUWA93
os-zhuang
added a commit
that referenced
this pull request
Jul 10, 2026
Ships the ADR-0090 v2 UI surface into the vendored console: spec-13 adoption + position relabel + external OWD dial + matrix OWD badges (objectui#2369), the D5 suggested-audience-binding install prompt + Access-pillar banner (objectui#2376, pairs with #2746), and the D6 'why can this user access?' explain panel (objectui#2375, pairs with #2743). Console rebuilt and bundle canary verified at this sha. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
摘要
补上 #2696 中标记为"缺失框架表面"的 D5 安装建议确认能力:包权限集声明
isDefault: true是安装时的建议("把此权限集绑定到everyone岗位"),此前服务端只在启动时把它读成 fallback 权限集名称——安装后既没有可查询的待确认建议列表,也没有确认/绑定端点。本 PR 提供两者,绝不自动绑定(D5 语义)。plugin-security
sys_audience_binding_suggestion(数据 API 只读;package_id × permission_set_name × anchor唯一;anchor 取值everyone/guest,为 D9 的 guest 泛化预留,当前唯一生产来源是isDefault→everyone)。syncAudienceBindingSuggestions:同时读取启动声明的 stack 元数据和已安装包的 manifest(registry 在installPackage时同步更新,所以运行时POST /api/v1/packages安装后立即可见,无需重启);声明消失(卸载/升级去掉标志)→ 删除 pending 行;绑定在别处已建立(启动基线绑定、管理员手工绑定)→ 标记confirmed(observed)。在启动、permission发布物化之后、以及每次 list 调用时运行。security服务新增listAudienceBindingSuggestions/confirmAudienceBindingSuggestion/dismissAudienceBindingSuggestion,三者都预先用租户级管理员(ADR-0066 超级用户通配符)门禁——受众锚点是租户级的(D12,委托管理员不可触碰)。sys_position_permission_set(绝不isSystem),因此 D5/D9 受众锚点门禁(高权限位拒绝)和 D12 委托管理门禁真实生效;另做一次describeAnchorForbiddenBits预检,让被拒时报错信息友好。本会话内刚安装、尚未物化的权限集,先经由与启动 seeder / 发布物化器相同的 provenance 检查 upsert(ADR-0086 D4)落库再绑定。HTTP 表面(rest + runtime 双层)
hono 开发/生产栈的
/api/v1由@objectstack/rest提供,适配器部署走 runtimeHttpDispatcher——两层都注册了路由,委托同一个security服务:GET /api/v1/security/suggested-bindings?status=&packageId=POST /api/v1/security/suggested-bindings/:id/confirmPOST /api/v1/security/suggested-bindings/:id/dismiss未认证 401;服务端类型化错误映射 403/404/409;未装 plugin-security 时 501(rest)/503(dispatcher)。
测试与验证
suggested-audience-bindings.test.ts(18 个用例):对账的幂等/剪枝/observed-confirm、confirm 的物化+调用者上下文断言、高权限位早期拒绝、外包名冲突拒绝、404/409、非管理员拒绝等。http-dispatcher.test.ts新增 6 个路由用例;rest 218 个用例全绿。pnpm turbo test --filter=...@objectstack/plugin-security --filter=...@objectstack/runtime全绿(69 个任务)。objectstack dev --seed-admin --fresh):匿名 list → 401;管理员 list → showcase 的第二个isDefault权限集(showcase_member_default)以 pending 出现;confirm → 200、bindingCreated: true、sys_position_permission_set行由调用者(非 system)创建;重复 confirm → 409。split('/').filter(Boolean))。文档
content/docs/permissions/permission-sets.mdx的isDefault小节补充了新表面与端点说明。关联
isDefaultinstall-suggestion confirm UI — no pending-suggestion list / confirm-bind API exists" 的框架侧事项。🤖 Generated with Claude Code
https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq