<!-- codsh-publication:grok-cli-dsh-rewrite:ticket-75 --> ## Parent https://github.com/Blackman99/codsh/issues/132 **Ticket:** 75 **Status:** ready-for-agent **User Stories:** 36, 60, 61(见父规格) ## What to build 已配置组织身份的用户连接远程 dsh 工作区时,登录、组织限制和凭据撤销实际作用于远端访问。 ## Acceptance criteria - [ ] 组织认证与远程连接明确映射,客户端不能仅凭已登录 UI 状态获得远端权限。 - [ ] 令牌过期、组织不匹配、撤销、重连和管理员锁定在实际请求中生效。 - [ ] 凭据用途/传递边界可审计,不能被不同配置目的地复用;错误不泄露令牌。 - [ ] 使用可部署身份/远端服务完成端到端验证;普通显式远程认证不等待该组织集成票。 ## Blocked by - #189 — Ticket 57: 可替代登录与组织服务 - #190 — Ticket 58: 远程工作区连接与执行 ## Delivery contract 用户于2026-09-20批准当前测试与任务拆分。按父规格的已安装产物/真实终端和公开协议测试验收;实际实现须带适用的文档、Changeset及必要检查。ready-for-agent表示任务已定义,不表示前置任务已完成,也不授权自动开始本轮实施、购买付费资源或发布软件版本。
Activity
feat(rust): organization identity for remote workspaces with a deploy…
Blackman99 commentedon Sep 25, 2026
已实现并快进推送到
rewrite/grok-dsh-20260920:bced29083d2a7f1212b83bb01e8fb7e88ed43e3b(快进范围5b300a8..bced290)。Rebase 说明:分支起点是
9bd0449(#191)。开发期间 #206(5b300a8,deep-research workflow)先合入,rebase 一次,没有冲突,两边的新增都保留了。#173 这期间没有推送。下列检查都在最终基线上重跑。做法:可部署的身份服务替身,不伪造官方设施
参考实现把已登录的组织 bearer 发给官方 Computer Hub。这里没有官方 Hub、SSO 或付费账号,所以映射为:组织自己部署的 OpenID Connect 服务(用真实的 Ory Hydra v26.2.0 验证,Apache-2.0,sqlite 版本,按发布页 checksums 校验)加上 #190 的 SSH 远端。组织的共享远端主机把组织密钥限制为强制命令(
restrict,command="…/org-agent.sh"),这个密钥能到达的只有codsh --rust agent --leader stdio。实现内容
rust/src/remote_identity.rs):requirements.toml(或managed_config.toml)中的[remote_access] identity = "required",加上issuer、audience、introspection_url(RFC 7662;https 或回环 http;官方 x.ai/grok.com 端点拒绝;可选introspection_client_id+ 只有属主可读的introspection_secret_file,Basic 认证)、teams、deny_subjects、recheck_secs(默认 30)、locked+lock_message、applies_to = "all"。优先级是 requirements > managed > user,用户的config.toml写identity = "off"无效。策略不完整或读不出来时,所有连接都拒绝(fail closed)。remote_access/remote_identity已加入策略键白名单。agent --leader stdio代理里):认证之前只放行initialize和authenticate(方法codsh-org-identity,令牌放在_meta."codsh/identity",由网关自己处理,不会转发给 leader)。其余请求都回-32000并带data.reason。initialize结果会注入authMethods(issuer、audience、teams),并在认证前去掉liveSessions。令牌要被 introspection 报告为 active,issuer 必须存在且一致,audience 一致,团队在允许列表中,而且不能是 refresh token。认证之后每个请求都重新向身份服务确认,连接期间还有定时复查。过期、在身份服务处撤销(包括codsh --rust logout)、deny_subjects、locked、连接中途换主体、连续 3 次认证失败,都会发出_codsh/remote_access_revoked、在 leader 上取消这个连接启动的轮次,然后断开。身份服务不可达或返回非 JSON 时拒绝;不跟随重定向。acp.rs、main.rs):只有 UI 登录状态不授予任何远程访问。codsh --rust login的会话只发给[[remote_identity]] target = "ssh://host[:port][/path]"中列出、audience与远端要求一致的目标(按完整路径段匹配)。issuer 也必须一致;如果 JWT 的aud指向别的受众,就不发送。每个请求前都重新读取 auth.json:会话刷新后重新认证,会话被清除或无法刷新时,请求在发出前就停下。空闲时令牌快过期会自动续期。收到撤销通知后显示原因,标记为断开,/reconnect会重新认证。remote check(含--json的identity字段)和/remote会显示是否要求身份、结果、主体和团队,或拒绝原因。remote-identity.log,主机remote-access.log,1 MiB 轮转),记录用途(audience/issuer)、目标及其配置来源、主体、团队、结果,以及令牌 SHA-256 的前 12 位十六进制指纹,从不记录令牌本身;错误信息也不含令牌。--codsh-remote-lifeline,退出码 2),因为普通 ssh 命令无法携带身份。没有策略的远端行为与 [Grok/dsh 58] 远程工作区连接与执行 #190 完全相同。CODSH_SHELL_SLEEP加入 dsh 子进程环境白名单(mock 模型的测试参数,之前实际没有传过去)。CODSH_TEST_HYDRA)、packages/cli/README.md、remote --help、changeset。验收对照
deny_subjects/locked,以及轮次进行中被撤销后远端命令被取消。检查(干净环境、最终基线
bced290)cargo fmt --check通过;pnpm typecheck通过。-D warnings:只有两处已存在的filesystem_sandbox(tail_marker、needless_return)报错;加-A dead_code -A clippy::needless_return后无告警。cargo test --workspace --no-fail-fast:bin 632 通过、4 失败(含新增 7 个remote_identity单元测试,其中一个用本机 introspection 端点验证 Basic 认证、secret 文件权限、refresh token 和缺少 issuer 的拒绝);其余 crate 498 + 42 + 61 + 2 + 379 + 61 全部通过,2 个 ignored。4 个失败都是已存在问题,涉及的文件本票都没改:filesystem_sandbox2 个,以及privacy::drafts_stay_local…和voice::substitute_route…(这两个测试捕获的 HTTP 请求体不完整,这次单独重跑也失败)。pnpm test(带CODSH_TEST_OPENSSH、CODSH_TEST_HYDRA):99 个文件,2533 通过、2 跳过、1 失败。失败的是reference-evidence(负载下 5 秒超时),用--testTimeout=60000单独重跑 11/11 通过。新增的scripts/rust-remote-identity.spec.mjs6/6 通过(真实 Hydra + 真实 sshd + 真实 dsh);rust-remote.spec.mjs和rust-clone.spec.mjs也通过。pnpm run build:rust之后):rust-remote-pty-test.py通过。rust-auth-pty-test.py只在 macOS 上运行,这里按设计退出,没有跑。本票没有新增 PTY 脚本。未验证 / 限制
/oauth2/device/code不一致,所以测试用的是回环 PKCE 登录,没有覆盖设备码登录。agent serve套接字不受网关约束。codsh --rust leader kill可以停止)。#207 阻塞的只有 #211,它仍被其他 11 个未关闭的票阻塞(其中 #206 已合入但还没关闭),所以本票关闭后没有新解锁的后续票。