Skip to main content

trellis_auth/
client.rs

1use trellis_client::{TrellisClient, UserConnectOptions};
2use trellis_sdk_auth::{
3    AuthClient as AuthApiClient, ListApprovalsRequest, RenewBindingTokenResponse,
4    RevokeApprovalRequest,
5};
6use crate::{
7    save_admin_session, AdminSessionState, ApprovalEntryRecord, AuthenticatedUser,
8    BoundSession, ServiceListEntry, TrellisAuthError,
9};
10
11/// Connect an authenticated admin client from the stored session state.
12pub async fn connect_admin_client_async(
13    state: &AdminSessionState,
14) -> Result<TrellisClient, TrellisAuthError> {
15    Ok(TrellisClient::connect_user(UserConnectOptions {
16        servers: &state.nats_servers,
17        sentinel_jwt: &state.sentinel_jwt,
18        sentinel_seed: &state.sentinel_seed,
19        session_key_seed_base64url: &state.session_seed,
20        binding_token: &state.binding_token,
21        timeout_ms: 5_000,
22    })
23    .await?)
24}
25
26/// Persist a renewed binding token and sentinel credentials into the admin session state.
27pub fn persist_renewed_admin_session(
28    state: &mut AdminSessionState,
29    renewed: RenewBindingTokenResponse,
30) -> Result<(), TrellisAuthError> {
31    let renewed = BoundSession {
32        binding_token: renewed.binding_token,
33        inbox_prefix: renewed.inbox_prefix,
34        expires: renewed.expires,
35        sentinel: renewed.sentinel,
36    };
37
38    state.binding_token = renewed.binding_token;
39    state.expires = renewed.expires;
40    state.sentinel_jwt = renewed.sentinel.jwt;
41    state.sentinel_seed = renewed.sentinel.seed;
42    save_admin_session(state)
43}
44
45/// Thin typed client for Trellis auth/admin RPCs used by the CLI.
46pub struct AuthClient<'a> {
47    inner: AuthApiClient<'a>,
48}
49
50impl<'a> AuthClient<'a> {
51    /// Wrap an already-connected low-level Trellis client.
52    pub fn new(inner: &'a TrellisClient) -> Self {
53        Self {
54            inner: AuthApiClient::new(inner),
55        }
56    }
57
58    /// Return the currently authenticated user.
59    pub async fn me(&self) -> Result<AuthenticatedUser, TrellisAuthError> {
60        Ok(self.inner.auth_me().await?.user)
61    }
62
63    /// List stored app approval decisions.
64    pub async fn list_approvals(
65        &self,
66        user: Option<&str>,
67        digest: Option<&str>,
68    ) -> Result<Vec<ApprovalEntryRecord>, TrellisAuthError> {
69        let request = ListApprovalsRequest {
70            user: user.map(ToOwned::to_owned),
71            digest: digest.map(ToOwned::to_owned),
72        };
73        Ok(self.inner.auth_list_approvals(&request).await?.approvals)
74    }
75
76    /// Revoke one stored approval decision.
77    pub async fn revoke_approval(
78        &self,
79        digest: &str,
80        user: Option<&str>,
81    ) -> Result<bool, TrellisAuthError> {
82        let request = RevokeApprovalRequest {
83            contract_digest: digest.to_string(),
84            user: user.map(ToOwned::to_owned),
85        };
86        Ok(self.inner.auth_revoke_approval(&request).await?.success)
87    }
88
89    /// List installed services.
90    pub async fn list_services(&self) -> Result<Vec<ServiceListEntry>, TrellisAuthError> {
91        Ok(self.inner.auth_list_services().await?.services)
92    }
93
94    /// Log out the current admin session remotely.
95    pub async fn logout(&self) -> Result<bool, TrellisAuthError> {
96        Ok(self.inner.auth_logout().await?.success)
97    }
98
99    /// Mint and persist a fresh binding token for the current session.
100    pub async fn renew_binding_token(
101        &self,
102        state: &mut AdminSessionState,
103    ) -> Result<(), TrellisAuthError> {
104        persist_renewed_admin_session(state, self.inner.auth_renew_binding_token().await?)
105    }
106}