You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit dc7f5c4
Browse filesBrowse the repository at this point in the historyBrowse files
feat(console): the Access policy page shows the address the server judges beside Allowed IP ranges (#31308)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XLcKzrqfQ4AkFLygR53Jiuf)_Fixes#31260
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:控制台「设置 → 访问策略」页(`access_policy` 命名空间)在「允许的 IP
范围」字段下方显示服务器判定的本次连接地址,读自组织管理员可调用的 `GET
/api/v1/auth/client-address`;`trustProxy` 为 false
时那句话说明「服务器不信任代理头,在代理之后这是代理的地址」。「身份验证」页照旧读平台管理员接口
`/admin/client-address`。
- 为什么改:组织的 IP
白名单保存时,若名单不含保存请求自己的地址会被拒;此前组织管理员只能从拒绝信息里得知自己的地址,存在把自己锁在组织外的风险。#31258
已提供服务端接口,本 PR 是控制台一半。
- 风险与回滚:只改 `@object-ui/console`
的设置页(patch);复用已有三条提示文案,不新增翻译键;读取失败(401/403/404/网络/形状不符)不显示提示、不弹
toast,页面与今天一致。回滚即 revert 本 PR。
-
先看:`ui/apps/console/src/pages/settings/SettingsView.tsx`(`CLIENT_ADDRESS_ROUTES`
与读取 effect)、同目录 `api.ts` 的 `getJudgedClientAddress(route)`、测试文件新增的
`describe`。
- 两分钟验证:`pnpm --dir ui exec vitest run
apps/console/src/pages/settings/__tests__/SettingsView.clientAddress-30313.test.tsx`,预期
18 条全绿(其中 9 条为本 PR 新增的访问策略页用例)。
- 需要你做的:无,席位落地(须在 #31258 合入 main、本分支 rebase 之后)。
## What changes
- **`api.ts`.** `getJudgedClientAddress` takes the route it reads, typed
`JudgedClientAddressRoute`: `'/admin/client-address'` (platform
administrators, the `auth` namespace) or `'/client-address'` (a caller
holding `setup.write` in their current organization, the `access_policy`
namespace). Both routes answer the same `{ success: true, data: {
clientAddress, trustProxy } }` from the same `runClientAddressRead` on
the server, so the envelope unwrap and the shape check are unchanged;
the malformed-body message names the route that was read. No
`@objectstack/client` method is added: the settings pages read
`/api/v1/auth` with this module's raw `fetch`, as the Authentication
page already did.
- **`SettingsView.tsx`.** The single `auth` constant becomes
`CLIENT_ADDRESS_ROUTES`, a map from namespace to route (`auth` and
`access_policy`), and `clientAddressRouteOf(namespace)` picks the read.
The read effect, the namespace tag on the answer, the note under
`allowed_ip_ranges` and the failure handling (a rejected read draws
nothing) are the existing ones.
- **Copy.** The three existing `console.settingsView.clientAddress*`
sentences are reused unchanged. Each holds for the organization list:
its save check is the same self-lock-out predicate as the deployment
list's (`allowedIpRangesWritePredicate` behind
`accessPolicyWritePredicate` for `allowed_ip_ranges`), judged against
the same address, so "a list that leaves it out cannot be saved from
here", the proxy sentence with `trustProxy` off, and "a non-empty list
cannot be saved" with no address are all true there. No i18n key is
added, removed or reworded.
- **Changeset** `.changeset/31260-access-policy-client-address.md`,
`@object-ui/console` patch (the console ships `dist`, the built SPA),
with both declaration lines.
## Tests
A new top-level `describe` in the page's existing harness,
`SettingsView.clientAddress-30313.test.tsx` (the whole page over a
stubbed `fetch` and the real `../api` module, expected sentences read
from the shipped `en` pack), 9 cases:
- `trustProxy: false`: the note under Allowed IP ranges is the direct
sentence (the proxy note), on that row only, named by the textarea's
`aria-describedby`; exactly one read, at `/api/v1/auth/client-address`,
with `credentials: 'include'`; the platform route is never asked.
- `trustProxy: true`: the via-proxy sentence. `clientAddress: null`: the
unknown sentence.
- Five failed reads, each leaving the page as it was (no note, no
`aria-describedby`, no load-error card, no toast): 403
`PERMISSION_DENIED`, 401 `UNAUTHENTICATED`, 404, a body missing
`clientAddress`, a network failure.
- The Authentication page still reads the platform route and never the
organization one.
## Acceptance notes
- **Docs.** `content/docs/permissions/authentication.mdx` describes the
Authentication page's note (the deployment list); `content/docs/**`
teaches no per-organization access policy at all (noted on #31258 too),
so no docs page names Setup → Access policy. Not changed here: outside
the claim's file surface.
## Verification
On head `bd882bc8` (base `7c523aa1`); every exit code captured before
any pipe.
<details><summary>Rendered tests: 38/38 files, 322/322 tests</summary>
- `pnpm --dir ui exec vitest run apps/console/src/pages/settings/` under
the verification lock: `Test Files 38 passed (38)`, `Tests 322 passed
(322)`, VERDICT command-exit 0.
- The edited file alone, verbose: `Tests 18 passed (18)`, the 9 new
cases listed by name.
- `pnpm --filter @object-ui/console run type-check` (after `pnpm
--filter '@object-ui/console^...' build`): VERDICT command-exit 0.
- `eslint` on the three edited source files from `ui/`: exit 0, 0 errors
(5 warnings, each on a `main` line this PR does not edit).
- `pnpm --dir ui check:new-line-citations`: 0 new citations, exit 0.
</details>
<details><summary>Ablation: the read removed, 8 red, restored to the
HEAD blob</summary>
`node scripts/ablation-replace.mjs --delete` on the map entry
`['access_policy', '/client-address'],` in `SettingsView.tsx` (console
tests import the page from `src/`, so no build sits between mutation and
run): anchor 1 → 0, blob `975d9c33` → `94aac0b5`; the edited test file
then failed 8 and passed 10 (every new access-policy case red, the
Authentication cases green). Restored: blob == HEAD (`975d9c33`), `git
diff HEAD` empty.
</details>
<details><summary>Derived gates: 35 of 35, all exit 0</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 35 commands, each run with stdin from
`/dev/null` (`pnpm --dir ui exec vitest run scripts/` under the lock:
`Test Files 170 passed | 2 skipped`, `Tests 4996 passed | 2 skipped`).
`--ran`: "35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED".
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(Not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (Except the maintainer
digest, which os-dev.md writes in Chinese.)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5o7LUAiCRZ7CgL8CKp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(console): the Access policy page shows the address the server judges beside Allowed IP ranges
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
An organization's `access_policy.allowed_ip_ranges` save is refused when the list leaves out the saving request's own address, and until now **Setup → Access policy** told the organization administrator that address only in the refusal. The page now reads `GET /api/v1/auth/client-address` (the organization administrator's read, answered to a caller holding `setup.write` in their current organization) and draws the note the Authentication page already draws under its own Allowed IP ranges: the address, and whether it came from a trusted proxy's forwarded headers or, with `trustProxy` off, from the connection itself (behind a proxy, the proxy's address); when the server cannot establish an address, that a non-empty list cannot be saved from here. The Authentication page keeps reading the platform administrator's route. A caller the route refuses, a server without the route, or any other failed read draws no note and no toast, and the page is otherwise unchanged. No translation key is added: the three existing note sentences are reused.
'Malformed response from GET /api/v1/auth/admin/client-address: expected an object carrying `clientAddress` (a string or null) and `trustProxy` (a boolean).',
263
+
`Malformed response from GET /api/v1/auth${route}: expected an object carrying \`clientAddress\` (a string or null) and \`trustProxy\` (a boolean).`,
0 commit comments