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
@@ -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
|
||||
- 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).
|
||||
|
||||
@@ -37,9 +37,10 @@ bridged straight in.
|
||||
jump host's own injected key excluded) or password (LDAP bind; the
|
||||
`ssh.passwordAuth` policy can restrict passwords to local clients or disable
|
||||
them — keys-only is recommended for a public host).
|
||||
2. **Authorization** — the hosts you may reach are the union of your LDAP groups
|
||||
× the SSO directory (`/api/discovery/resources?group=<cn>`). No directory
|
||||
entry, no access.
|
||||
2. **Authorization** — the jump host calls the SSO Manager's
|
||||
`GET /api/discovery/access/:uid` once per user; the SSO evaluates the
|
||||
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
|
||||
your `sshPublicKey` in LDAP (comment-marked), then connects downstream **as
|
||||
you** using its private key. Downstream hosts already serve keys from LDAP
|
||||
|
||||
@@ -41,11 +41,13 @@ Every attempt — success or failure, with method and reason — is audited.
|
||||
## 2. Access & target resolution
|
||||
|
||||
The hosts a user may reach are computed from the directory, not a local list:
|
||||
|
||||
1. The user's LDAP group memberships (`(&(objectClass=groupOfNames)(member=…))`).
|
||||
2. For each group, the SSO's
|
||||
`GET /api/discovery/resources?group=<cn>` (authenticated with an API token),
|
||||
unioned and filtered to `kind: host`.
|
||||
the jump host calls the SSO's `GET /api/discovery/access/:uid` (authenticated
|
||||
with an API token) once per user; the SSO evaluates the user's LDAP group
|
||||
memberships server-side and returns the full access projection in one
|
||||
response, already filtered to `kind: host`. (The jump host also has an
|
||||
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
|
||||
`metadata.address`) and port `metadata.sshPort` (default 22). Results are cached
|
||||
|
||||
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "t42-jump-host",
|
||||
"version": "1.19.0",
|
||||
"version": "1.19.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "t42-jump-host",
|
||||
"version": "1.19.0",
|
||||
"version": "1.19.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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",
|
||||
"author": [
|
||||
{
|
||||
|
||||