From 5f7e29cec4333715346b4bf3195633db93449d9d Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 9 Nov 2026 17:35:12 +0000 Subject: auth: whoami says what the request may do `GET /auth/whoami` lists the user's capabilities (`caps`) and its token's `limit`, if any, side by side; what the request may do is what both give, as endpoints decide it. It says so now (`may`): the capabilities, or with a limit each capability met with each of the limit's (`Capability::meet`): at the deeper of the two paths, a `*` of one taking the other's key there, with the rights both name; nothing where the paths part, no right is shared, or the path is protocol data one of them doesn't name. --- src/api/login.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/api/login.rs') diff --git a/src/api/login.rs b/src/api/login.rs index 0dc74e5..694ad0a 100644 --- a/src/api/login.rs +++ b/src/api/login.rs @@ -715,6 +715,18 @@ pub(crate) async fn whoami( let swarm = state.swarm_id().await; let replica = state.doc.read().await; let entry = registry::user(&replica.doc, &user.0); + // What the request may do: the user's capabilities, and with a token saying less, + // what both give (`Capability::meet`), as `api::access::authorize` decides. + let may = entry.as_ref().map(|entry| { + match token.as_ref().and_then(|token| token.claims.limit.as_ref()) { + None => entry.caps.values().cloned().collect::>(), + Some(limit) => entry + .caps + .values() + .flat_map(|cap| limit.iter().filter_map(|outer| cap.meet(outer))) + .collect(), + } + }); let token = token.map(|Extension(token)| { json!({ "issuer": token.claims.issuer, @@ -732,6 +744,7 @@ pub(crate) async fn whoami( "via": via.0, "registry": registry::exists(&replica.doc), "caps": entry.as_ref().map(|entry| &entry.caps), + "may": may, "disabled": entry.as_ref().map(|entry| entry.disabled), "token": token, })) -- cgit v1.3.1