docs: correct host-access authorization model description

README.md and docs/architecture.md described authorization as a client-side
loop over each of a user's LDAP groups, calling the SSO's
GET /api/discovery/resources?group=<cn> once per group. The actual code
(utils/access.js, accessibleHosts()) makes a single call to the SSO's
GET /api/discovery/access/:uid, which resolves the user's groups
server-side and returns the full access projection in one response.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113gCdnfSCuZr6xvPDxTo3D
This commit is contained in:
2026-08-06 21:27:31 -04:00
parent 57d0600fc0
commit 24a6a718e0
9 changed files with 17 additions and 11 deletions
+3
View File
@@ -1,3 +1,6 @@
## v1.19.1
- docs: README.md and docs/architecture.md described host-access authorization as a client-side loop over each of a user's LDAP groups (`GET /api/discovery/resources?group=<cn>` per group). The actual code (`utils/access.js`, `accessibleHosts()`) makes one call to the SSO's `GET /api/discovery/access/:uid`, which resolves the user's groups server-side. Corrected both.
## v1.19.0 ## v1.19.0
- fix: **only catalog hosts are jump targets.** `isManagedHost` treated a missing `metadata.managed` flag as permission, so any host the SSO merely *discovered* — an unpromoted Proxmox guest, a UniFi client — was offered in the TUI picker and accepted by the username grammar. The filter is now `isCatalogHost`, mirroring the SSO Directory's own rule: a resource carrying `discovery_sources` but never promoted is excluded, while hand-created hosts (no `discovery_sources`) and promoted ones (`managed: true`) are included, and an explicit `managed: false` is always excluded. - fix: **only catalog hosts are jump targets.** `isManagedHost` treated a missing `metadata.managed` flag as permission, so any host the SSO merely *discovered* — an unpromoted Proxmox guest, a UniFi client — was offered in the TUI picker and accepted by the username grammar. The filter is now `isCatalogHost`, mirroring the SSO Directory's own rule: a resource carrying `discovery_sources` but never promoted is excluded, while hand-created hosts (no `discovery_sources`) and promoted ones (`managed: true`) are included, and an explicit `managed: false` is always excluded.
- test: regression coverage for all five cases (hand-made, discovered-unpromoted, discovered-promoted, `manual` source, explicitly unmanaged). - test: regression coverage for all five cases (hand-made, discovered-unpromoted, discovered-promoted, `manual` source, explicitly unmanaged).
+4 -3
View File
@@ -37,9 +37,10 @@ bridged straight in.
jump host's own injected key excluded) or password (LDAP bind; the jump host's own injected key excluded) or password (LDAP bind; the
`ssh.passwordAuth` policy can restrict passwords to local clients or disable `ssh.passwordAuth` policy can restrict passwords to local clients or disable
them — keys-only is recommended for a public host). them — keys-only is recommended for a public host).
2. **Authorization** — the hosts you may reach are the union of your LDAP groups 2. **Authorization** — the jump host calls the SSO Manager's
× the SSO directory (`/api/discovery/resources?group=<cn>`). No directory `GET /api/discovery/access/:uid` once per user; the SSO evaluates the
entry, no access. user's LDAP group memberships server-side and returns their full access
projection in one response. No directory entry, no access.
3. **Key injection** — on first use the jump host appends its own public key to 3. **Key injection** — on first use the jump host appends its own public key to
your `sshPublicKey` in LDAP (comment-marked), then connects downstream **as your `sshPublicKey` in LDAP (comment-marked), then connects downstream **as
you** using its private key. Downstream hosts already serve keys from LDAP you** using its private key. Downstream hosts already serve keys from LDAP
+7 -5
View File
@@ -41,11 +41,13 @@ Every attempt — success or failure, with method and reason — is audited.
## 2. Access & target resolution ## 2. Access & target resolution
The hosts a user may reach are computed from the directory, not a local list: The hosts a user may reach are computed from the directory, not a local list:
the jump host calls the SSO's `GET /api/discovery/access/:uid` (authenticated
1. The user's LDAP group memberships (`(&(objectClass=groupOfNames)(member=…))`). with an API token) once per user; the SSO evaluates the user's LDAP group
2. For each group, the SSO's memberships server-side and returns the full access projection in one
`GET /api/discovery/resources?group=<cn>` (authenticated with an API token), response, already filtered to `kind: host`. (The jump host also has an
unioned and filtered to `kind: host`. admin-only `allHosts()` path, used for the unfiltered catalog listing, which
does call `GET /api/discovery/resources?group=<cn>` per group — but that's
not the per-user authorization path.)
Each host's dial address is `metadata.ip` (or the hostname from Each host's dial address is `metadata.ip` (or the hostname from
`metadata.address`) and port `metadata.sshPort` (default 22). Results are cached `metadata.address`) and port `metadata.sshPort` (default 22). Results are cached
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "t42-jump-host", "name": "t42-jump-host",
"version": "1.19.0", "version": "1.19.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "t42-jump-host", "name": "t42-jump-host",
"version": "1.19.0", "version": "1.19.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^7.3.0", "@fortawesome/fontawesome-free": "^7.3.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "t42-jump-host", "name": "t42-jump-host",
"version": "1.19.0", "version": "1.19.1",
"description": "SSH jump host for the theta42 stack — LDAP-authenticated, directory-driven host bridging with audit and metrics", "description": "SSH jump host for the theta42 stack — LDAP-authenticated, directory-driven host bridging with audit and metrics",
"author": [ "author": [
{ {