cloud_sdk/authentication/
attempt_owned.rs1use alloc::sync::Arc;
2
3use super::attempt::{
4 CredentialAttemptError, CredentialAttemptGeneration, CredentialAttemptStatus,
5 CredentialDispatchGuard, CredentialReconfirmation, SharedCredentialAttemptState,
6};
7
8pub struct OwnedCredentialAttempt {
13 owner: Arc<SharedCredentialAttemptState>,
14 generation: CredentialAttemptGeneration,
15}
16
17impl OwnedCredentialAttempt {
18 #[must_use]
20 pub const fn generation(&self) -> CredentialAttemptGeneration {
21 self.generation
22 }
23}
24
25impl core::fmt::Debug for OwnedCredentialAttempt {
26 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27 formatter
28 .debug_struct("OwnedCredentialAttempt")
29 .field("owner", &"[bound]")
30 .field("generation", &self.generation)
31 .finish()
32 }
33}
34
35impl PartialEq for OwnedCredentialAttempt {
36 fn eq(&self, other: &Self) -> bool {
37 Arc::ptr_eq(&self.owner, &other.owner) && self.generation == other.generation
38 }
39}
40
41impl Eq for OwnedCredentialAttempt {}
42
43pub struct OwnedCredentialAttemptState {
48 state: Arc<SharedCredentialAttemptState>,
49}
50
51impl OwnedCredentialAttemptState {
52 #[must_use]
54 pub fn new() -> Self {
55 Self {
56 state: Arc::new(SharedCredentialAttemptState::new()),
57 }
58 }
59
60 #[must_use]
62 pub fn observe(&self) -> (CredentialAttemptGeneration, CredentialAttemptStatus) {
63 self.state.observe()
64 }
65
66 pub fn begin(&self) -> Result<OwnedCredentialAttempt, CredentialAttemptError> {
68 let attempt = self.state.begin()?;
69 Ok(OwnedCredentialAttempt {
70 owner: Arc::clone(&self.state),
71 generation: attempt.generation(),
72 })
73 }
74
75 pub fn validate(&self, attempt: &OwnedCredentialAttempt) -> Result<(), CredentialAttemptError> {
77 self.validate_owner(attempt)?;
78 self.state.validate_generation(attempt.generation)
79 }
80
81 pub fn reserve_dispatch(
83 &self,
84 attempt: &OwnedCredentialAttempt,
85 ) -> Result<CredentialDispatchGuard<'_>, CredentialAttemptError> {
86 self.validate_owner(attempt)?;
87 self.state.reserve_generation(attempt.generation)
88 }
89
90 pub fn reject(&self, attempt: &OwnedCredentialAttempt) -> Result<(), CredentialAttemptError> {
92 self.validate_owner(attempt)?;
93 self.state.reject_generation(attempt.generation)
94 }
95
96 pub fn replace(
98 &self,
99 expected: CredentialAttemptGeneration,
100 ) -> Result<CredentialAttemptGeneration, CredentialAttemptError> {
101 self.state.replace(expected)
102 }
103
104 pub fn reconfirm(
106 &self,
107 expected: CredentialAttemptGeneration,
108 acknowledgement: CredentialReconfirmation,
109 ) -> Result<CredentialAttemptGeneration, CredentialAttemptError> {
110 self.state.reconfirm(expected, acknowledgement)
111 }
112
113 fn validate_owner(
114 &self,
115 attempt: &OwnedCredentialAttempt,
116 ) -> Result<(), CredentialAttemptError> {
117 if !Arc::ptr_eq(&self.state, &attempt.owner) {
118 return Err(CredentialAttemptError::ForeignState);
119 }
120 Ok(())
121 }
122}
123
124impl Default for OwnedCredentialAttemptState {
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130impl core::fmt::Debug for OwnedCredentialAttemptState {
131 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132 let (generation, status) = self.observe();
133 formatter
134 .debug_struct("OwnedCredentialAttemptState")
135 .field("owner", &"[bound]")
136 .field("generation", &generation)
137 .field("status", &status)
138 .finish()
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::OwnedCredentialAttemptState;
145 use crate::authentication::{CredentialAttemptError, CredentialAttemptGeneration};
146
147 #[test]
148 fn owned_attempts_reject_foreign_and_stale_lineages() {
149 let owner_a = OwnedCredentialAttemptState::new();
150 let owner_b = OwnedCredentialAttemptState::new();
151 let attempt = owner_a
152 .begin()
153 .unwrap_or_else(|_| unreachable!("initial owned attempt was rejected"));
154
155 assert_eq!(
156 owner_b.validate(&attempt),
157 Err(CredentialAttemptError::ForeignState)
158 );
159 assert_eq!(
160 owner_b.reject(&attempt),
161 Err(CredentialAttemptError::ForeignState)
162 );
163 let replacement = owner_a
164 .replace(CredentialAttemptGeneration::INITIAL)
165 .unwrap_or_else(|_| unreachable!("owned replacement was rejected"));
166 assert_eq!(replacement.get(), 2);
167 assert_eq!(
168 owner_a.reject(&attempt),
169 Err(CredentialAttemptError::StaleGeneration)
170 );
171 }
172}