From b29beec9e91bcaeb32d7e5147df1153274b963cf Mon Sep 17 00:00:00 2001 From: Michael Netshipise Date: Fri, 17 Jul 2026 03:48:22 +0200 Subject: [PATCH] =?UTF-8?q?admin:=20get=5Frole=5Fgrant=5Fhistory=20wrapper?= =?UTF-8?q?=20=E2=80=94=20v0.2.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture-audit directory: every user who EVER held one of role_names for a target, revoked grants included (include_deleted); actor-gated server-side (untargeted = all targets, or targeted at target_id). PII-free (user_id + display_name). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01JE3r42YdE8tt7vV5zPdJdE --- proto/st-peter-admin.proto | 23 +++++++++++++++++++++++ rust/Cargo.toml | 2 +- rust/src/lib.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/proto/st-peter-admin.proto b/proto/st-peter-admin.proto index 1131019..f8cf139 100644 --- a/proto/st-peter-admin.proto +++ b/proto/st-peter-admin.proto @@ -30,6 +30,10 @@ service AuthAdminService { rpc GetRoleScopes (GetRoleScopesRequest) returns (GetRoleScopesResponse); rpc GetUserWithRoles (GetUserWithRolesRequest) returns (GetUserWithRolesResponse); rpc GetAssignableRoles (GetAssignableRolesRequest) returns (GetAssignableRolesResponse); + // §role-grant-history — everyone who EVER held one of role_names for + // target_id (revoked grants included when include_deleted). Actor-gated: + // requested roles untargeted (all targets) or targeted at target_id. + rpc GetRoleGrantHistory (GetRoleGrantHistoryRequest) returns (GetRoleGrantHistoryResponse); rpc UpdateUserInfo (UpdateUserInfoRequest) returns (UpdateUserInfoResponse); rpc GetScopeAncestors (GetScopeAncestorsRequest) returns (GetScopeAncestorsResponse); rpc GetScopeDescendants (GetScopeDescendantsRequest) returns (GetScopeDescendantsResponse); @@ -554,3 +558,22 @@ message ClearUserSessionsResponse { string message = 3; int32 cleared_count = 4; } + +message GetRoleGrantHistoryRequest { + string actor_token = 1; + repeated string role_names = 2; + string target_id = 3; // uuid of the entity (e.g. an advertiser) + bool include_deleted = 4; +} + +message RoleGrantUser { + string user_id = 1; + string display_name = 2; +} + +message GetRoleGrantHistoryResponse { + bool success = 1; + ResultCode result_code = 2; + string message = 3; + repeated RoleGrantUser users = 4; +} diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 7e8de81..8fcf51c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "st-peter-client" -version = "0.2.3" +version = "0.2.4" edition = "2021" description = "Official Rust client for st-peter (aura-users) — authentication over gRPC with a token-verify cache" repository = "https://git.awesomike.com/pub/st-peter-client" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 5ea1bcf..c3d9a02 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -427,6 +427,35 @@ impl AdminClient { Ok(resp.users) } + /// §role-grant-history — everyone who EVER held one of `role_names` for + /// `target_id` (revoked grants included when `include_deleted`). Gated by + /// the ACTOR's own token: any requested role untargeted (all targets) or + /// targeted at `target_id`; otherwise the server returns FORBIDDEN in the + /// response envelope. Returns (user_id, display_name), PII-free. + pub async fn get_role_grant_history( + &self, + actor_token: &str, + role_names: Vec, + target_id: &str, + include_deleted: bool, + ) -> Result> { + let resp = self + .inner + .clone() + .get_role_grant_history(adminpb::GetRoleGrantHistoryRequest { + actor_token: actor_token.to_string(), + role_names, + target_id: target_id.to_string(), + include_deleted, + }) + .await? + .into_inner(); + if !resp.success { + return Err(Error::Rejected { code: resp.result_code, message: resp.message }); + } + Ok(resp.users) + } + /// Assign a role to a user — targeted when `target_id` is set (the /// multi-tenancy device: e.g. `cms-content-editor` for one organization), /// optionally time-bound via `expires_at`.