cloud_sdk/authentication/
attempt_owned.rs1use alloc::sync::Arc;
2
3use super::attempt::{
4 CredentialAttemptError, CredentialAttemptGeneration, CredentialAttemptStatus,
5 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 reject(&self, attempt: &OwnedCredentialAttempt) -> Result<(), CredentialAttemptError> {
83 self.validate_owner(attempt)?;
84 self.state.reject_generation(attempt.generation)
85 }
86
87 pub fn replace(
89 &self,
90 expected: CredentialAttemptGeneration,
91 ) -> Result<CredentialAttemptGeneration, CredentialAttemptError> {
92 self.state.replace(expected)
93 }
94
95 pub fn reconfirm(
97 &self,
98 expected: CredentialAttemptGeneration,
99 acknowledgement: CredentialReconfirmation,
100 ) -> Result<CredentialAttemptGeneration, CredentialAttemptError> {
101 self.state.reconfirm(expected, acknowledgement)
102 }
103
104 fn validate_owner(
105 &self,
106 attempt: &OwnedCredentialAttempt,
107 ) -> Result<(), CredentialAttemptError> {
108 if !Arc::ptr_eq(&self.state, &attempt.owner) {
109 return Err(CredentialAttemptError::ForeignState);
110 }
111 Ok(())
112 }
113}
114
115impl Default for OwnedCredentialAttemptState {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl core::fmt::Debug for OwnedCredentialAttemptState {
122 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 let (generation, status) = self.observe();
124 formatter
125 .debug_struct("OwnedCredentialAttemptState")
126 .field("owner", &"[bound]")
127 .field("generation", &generation)
128 .field("status", &status)
129 .finish()
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::OwnedCredentialAttemptState;
136 use crate::authentication::{CredentialAttemptError, CredentialAttemptGeneration};
137
138 #[test]
139 fn owned_attempts_reject_foreign_and_stale_lineages() {
140 let owner_a = OwnedCredentialAttemptState::new();
141 let owner_b = OwnedCredentialAttemptState::new();
142 let attempt = owner_a
143 .begin()
144 .unwrap_or_else(|_| unreachable!("initial owned attempt was rejected"));
145
146 assert_eq!(
147 owner_b.validate(&attempt),
148 Err(CredentialAttemptError::ForeignState)
149 );
150 assert_eq!(
151 owner_b.reject(&attempt),
152 Err(CredentialAttemptError::ForeignState)
153 );
154 let replacement = owner_a
155 .replace(CredentialAttemptGeneration::INITIAL)
156 .unwrap_or_else(|_| unreachable!("owned replacement was rejected"));
157 assert_eq!(replacement.get(), 2);
158 assert_eq!(
159 owner_a.reject(&attempt),
160 Err(CredentialAttemptError::StaleGeneration)
161 );
162 }
163}