Skip to main content

cloud_sdk/authentication/
transport.rs

1use core::fmt;
2use core::future::Future;
3
4use crate::transport::{
5    AsyncExecutionError, AsyncResponseStaging, RawResponsePolicy, ResponseCompletion,
6    ResponseWriter, TransportRequest,
7};
8
9use super::AuthenticationScopePolicy;
10
11/// Request plus mandatory provider or operation-owned authentication policy.
12///
13/// Construction is internal so application code cannot bypass prepared or
14/// permit-authorized execution through the public transport traits.
15///
16/// ```compile_fail
17/// use cloud_sdk::authentication::{AuthenticatedRequest, AuthenticationScopePolicy};
18/// use cloud_sdk::transport::{RawResponsePolicy, TransportRequest};
19///
20/// fn forge(
21///     request: TransportRequest<'_>,
22///     authentication: AuthenticationScopePolicy<'_>,
23///     response: RawResponsePolicy<'_>,
24/// ) {
25///     let _ = AuthenticatedRequest::new(request, authentication, &response);
26/// }
27/// ```
28#[derive(Clone, Copy)]
29pub struct AuthenticatedRequest<'request, 'policy> {
30    request: TransportRequest<'request>,
31    policy: AuthenticationScopePolicy<'policy>,
32    response_policy: RawResponsePolicy<'policy>,
33}
34
35impl<'request, 'policy> AuthenticatedRequest<'request, 'policy> {
36    /// Binds a transport request to its complete authentication policy.
37    #[must_use]
38    pub(crate) const fn new(
39        request: TransportRequest<'request>,
40        policy: AuthenticationScopePolicy<'policy>,
41        response_policy: &RawResponsePolicy<'policy>,
42    ) -> Self {
43        Self {
44            request,
45            policy,
46            response_policy: *response_policy,
47        }
48    }
49
50    /// Returns the credential-free transport request.
51    #[must_use]
52    pub const fn transport_request(self) -> TransportRequest<'request> {
53        self.request
54    }
55
56    /// Returns the complete authentication policy.
57    #[must_use]
58    pub const fn policy(self) -> AuthenticationScopePolicy<'policy> {
59        self.policy
60    }
61
62    /// Returns the complete status-class raw response policy.
63    #[must_use]
64    pub const fn response_policy(self) -> RawResponsePolicy<'policy> {
65        self.response_policy
66    }
67}
68
69impl fmt::Debug for AuthenticatedRequest<'_, '_> {
70    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
71        formatter
72            .debug_struct("AuthenticatedRequest")
73            .field("request", &self.request)
74            .field("policy", &self.policy)
75            .field("response_policy", &self.response_policy)
76            .finish()
77    }
78}
79
80/// Blocking transport that cannot execute without an authentication policy.
81pub trait BlockingAuthenticatedTransport {
82    /// Transport-specific failure.
83    type Error;
84
85    /// Validates scope and sends one authenticated request.
86    fn send_authenticated(
87        &self,
88        request: AuthenticatedRequest<'_, '_>,
89        response: &mut ResponseWriter<'_>,
90    ) -> Result<(), Self::Error>;
91}
92
93/// Executor-neutral Send async transport requiring authentication policy.
94///
95/// Implementations stage responses without commit access. Callers use
96/// [`drive_async_authenticated`].
97pub trait AsyncAuthenticatedTransport {
98    /// Transport-specific failure.
99    type Error;
100
101    /// Validates scope and stages one authenticated response.
102    fn send_authenticated<'transport, 'request, 'policy, 'writer, 'buffer>(
103        &'transport self,
104        request: AuthenticatedRequest<'request, 'policy>,
105        response: AsyncResponseStaging<'writer, 'buffer>,
106    ) -> impl Future<Output = Result<ResponseCompletion, Self::Error>> + Send + 'writer
107    where
108        'transport: 'writer,
109        'request: 'writer,
110        'policy: 'writer,
111        'buffer: 'writer;
112}
113
114/// Executor-neutral authenticated transport for `!Send` local futures.
115///
116/// Dropping the returned future leaves the response uncommitted and clears
117/// partial response state, but does not prove that the authenticated request
118/// was not delivered. Cancellation is conservatively
119/// [`DeliveryPhase::PossiblySent`](crate::transport::DeliveryPhase::PossiblySent).
120pub trait LocalAsyncAuthenticatedTransport {
121    /// Transport-specific failure.
122    type Error;
123
124    /// Validates scope and sends one authenticated request locally.
125    fn send_authenticated_local<'transport, 'request, 'policy, 'writer, 'buffer>(
126        &'transport self,
127        request: AuthenticatedRequest<'request, 'policy>,
128        response: AsyncResponseStaging<'writer, 'buffer>,
129    ) -> impl Future<Output = Result<ResponseCompletion, Self::Error>> + 'writer
130    where
131        'transport: 'writer,
132        'request: 'writer,
133        'policy: 'writer,
134        'buffer: 'writer;
135}
136
137/// Drives one authenticated local attempt and commits after `Ready(Ok)`.
138pub async fn drive_local_authenticated<'transport, 'request, 'policy, 'writer, 'buffer, T>(
139    transport: &'transport T,
140    request: AuthenticatedRequest<'request, 'policy>,
141    response: &'writer mut ResponseWriter<'buffer>,
142) -> Result<(), AsyncExecutionError<T::Error>>
143where
144    T: LocalAsyncAuthenticatedTransport + ?Sized,
145    'transport: 'writer,
146    'request: 'writer,
147    'policy: 'writer,
148    'buffer: 'writer,
149{
150    let mut attempt = response
151        .begin_attempt()
152        .map_err(AsyncExecutionError::Response)?;
153    let completion = transport
154        .send_authenticated_local(request, attempt.staging())
155        .await
156        .map_err(AsyncExecutionError::Transport)?;
157    attempt
158        .commit_completion(completion)
159        .map_err(AsyncExecutionError::Response)
160}
161
162/// Drives one authenticated cross-thread async attempt and commits after `Ready(Ok)`.
163pub async fn drive_async_authenticated<'transport, 'request, 'policy, 'writer, 'buffer, T>(
164    transport: &'transport T,
165    request: AuthenticatedRequest<'request, 'policy>,
166    response: &'writer mut ResponseWriter<'buffer>,
167) -> Result<(), AsyncExecutionError<T::Error>>
168where
169    T: AsyncAuthenticatedTransport + ?Sized,
170    'transport: 'writer,
171    'request: 'writer,
172    'policy: 'writer,
173    'buffer: 'writer,
174{
175    let mut attempt = response
176        .begin_attempt()
177        .map_err(AsyncExecutionError::Response)?;
178    let completion = transport
179        .send_authenticated(request, attempt.staging())
180        .await
181        .map_err(AsyncExecutionError::Transport)?;
182    attempt
183        .commit_completion(completion)
184        .map_err(AsyncExecutionError::Response)
185}
186
187impl<T> LocalAsyncAuthenticatedTransport for T
188where
189    T: AsyncAuthenticatedTransport + ?Sized,
190{
191    type Error = T::Error;
192
193    async fn send_authenticated_local<'transport, 'request, 'policy, 'writer, 'buffer>(
194        &'transport self,
195        request: AuthenticatedRequest<'request, 'policy>,
196        response: AsyncResponseStaging<'writer, 'buffer>,
197    ) -> Result<ResponseCompletion, Self::Error>
198    where
199        'transport: 'writer,
200        'request: 'writer,
201        'policy: 'writer,
202        'buffer: 'writer,
203    {
204        AsyncAuthenticatedTransport::send_authenticated(self, request, response).await
205    }
206}