1use serde::{Deserialize, Serialize};
5
6use crate::objects::ExactObjectRef;
7use crate::provider::StoreMemberProviderAccessGrant;
8use crate::store_commit::device_join_exchange::{
9 DeviceJoinAbandonment, DeviceJoinActivation, DeviceJoinOffer, DeviceJoinReadiness,
10 DeviceProviderAccessRequest, DeviceProviderAdmissionApproval,
11 DeviceProviderAdmissionCompletion, DeviceRegistrationRequest, ProviderReadyDeviceBootstrap,
12 ProvisionalDeviceBootstrap, SamePrincipalDeviceJoin,
13};
14
15use super::*;
16use crate::store_commit::DeviceJoinAbandonmentRef;
17
18#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum DeviceJoinStatus {
22 AwaitingAccessRequest {
23 offer: DeviceJoinOffer,
24 },
25 AwaitingProviderAdmission {
26 request: DeviceProviderAccessRequest,
27 },
28 AwaitingRegistrationRequest {
29 approval: DeviceProviderAdmissionApproval,
30 },
31 AwaitingBootstrap {
32 request: DeviceRegistrationRequest,
33 },
34 SamePrincipalActivationCreatePending {
35 request: DeviceRegistrationRequest,
36 },
37 AwaitingChallengePublication {
38 bootstrap: ProvisionalDeviceBootstrap,
39 },
40 AwaitingReadiness {
41 bootstrap: ProviderReadyDeviceBootstrap,
42 },
43 AwaitingProviderCompletion {
44 readiness: DeviceJoinReadiness,
45 },
46 AwaitingActivation {
47 completion: DeviceProviderAdmissionCompletion,
48 },
49 AwaitingCompletion {
50 activation: DeviceJoinActivation,
51 },
52 SamePrincipalCompleted {
53 join: SamePrincipalDeviceJoin,
54 },
55 Abandoned {
56 abandonment: DeviceJoinAbandonment,
57 },
58 ProviderAccessGrantCreatePending {
59 request: DeviceProviderAccessRequest,
60 grant: StoreMemberProviderAccessGrant,
61 },
62 AbandonmentCreatePending {
63 abandonment: DeviceJoinAbandonmentRef,
64 },
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "snake_case", deny_unknown_fields)]
69pub enum DeviceJoinAction {
70 TransferOffer(DeviceJoinOffer),
71 TransferProviderAccessRequest(DeviceProviderAccessRequest),
72 TransferProviderAdmissionApproval(DeviceProviderAdmissionApproval),
73 TransferRegistrationRequest(DeviceRegistrationRequest),
74 TransferProviderReadyBootstrap(ProviderReadyDeviceBootstrap),
75 TransferReadiness(DeviceJoinReadiness),
76 TransferSamePrincipalJoin(SamePrincipalDeviceJoin),
77 TransferActivation(DeviceJoinActivation),
78 TransferAbandonment(DeviceJoinAbandonment),
79 CompleteJoin(DeviceJoinActivation),
80 ResumeOperation {
81 attempt_id: DeviceJoinAttemptId,
82 role: DeviceJoinRole,
83 },
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
87#[serde(rename_all = "snake_case", deny_unknown_fields)]
88pub enum OwnerJoinProgress {
89 Offered(DeviceJoinOffer),
90 AccessRequested(DeviceProviderAccessRequest),
94 AccessGrantPrepared {
95 request: DeviceProviderAccessRequest,
96 grant: StoreMemberProviderAccessGrant,
97 prepared: PreparedDeviceJoinObject,
98 },
99 ApprovalPrepared(DeviceProviderAdmissionApproval),
100 RegistrationRequested(DeviceRegistrationRequest),
101 AbandonmentCreateIntent {
102 offer: DeviceJoinOffer,
103 abandonment: DeviceJoinAbandonmentRef,
104 prepared: PreparedDeviceJoinObject,
105 },
106 AttemptActivated(ProvisionalDeviceBootstrap),
107 ChallengeCreateIntent(ProvisionalDeviceBootstrap),
108 ProviderReady(ProviderReadyDeviceBootstrap),
109 ResponseObserved(DeviceJoinReadiness),
110 Completed(DeviceProviderAdmissionCompletion),
111 SamePrincipalActivationCreateIntent {
112 request: DeviceRegistrationRequest,
113 bootstrap_cut: StoreHistoryCut,
114 membership: StoreMembershipStateRef,
115 registration: StoreDeviceRegistrationRef,
116 registration_prepared: PreparedDeviceJoinObject,
117 },
118 ActivationCreateIntent {
119 completion: DeviceProviderAdmissionCompletion,
120 },
121 ActivationPrepared {
130 completion: DeviceProviderAdmissionCompletion,
131 activation: DeviceJoinActivation,
132 registration: StoreDeviceRegistrationRef,
133 },
134 SamePrincipalCompleted {
142 join: SamePrincipalDeviceJoin,
143 registration: StoreDeviceRegistrationRef,
144 },
145 Abandoned(DeviceJoinAbandonment),
146}
147
148#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(deny_unknown_fields)]
150pub struct PreparedDeviceJoinObject {
151 pub object: ExactObjectRef,
152 pub stored_bytes: Vec<u8>,
153}
154
155impl PreparedDeviceJoinObject {
156 pub fn from_prepared(prepared: &crate::objects::PreparedExactObject) -> Self {
157 Self {
158 object: prepared.reference().clone(),
159 stored_bytes: prepared.stored_bytes().to_vec(),
160 }
161 }
162
163 pub fn restore(
167 &self,
168 ) -> Result<crate::objects::PreparedExactObject, crate::objects::StorageError> {
169 crate::objects::PreparedExactObject::new(self.object.clone(), self.stored_bytes.clone())
170 }
171}
172
173#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(rename_all = "snake_case", deny_unknown_fields)]
175pub enum JoinerJoinProgress {
176 OfferReceived(DeviceJoinOffer),
177 AccessRequested(DeviceProviderAccessRequest),
178 ApprovalReceived(DeviceProviderAdmissionApproval),
179 RegistrationPrepared(DeviceRegistrationRequest),
180 Ready(DeviceJoinReadiness),
181 ActivationObserved {
182 readiness: DeviceJoinReadiness,
183 activation: DeviceJoinActivation,
184 },
185}
186
187#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
188#[serde(rename_all = "snake_case", deny_unknown_fields)]
189pub enum DeviceJoinRoleProgress {
190 Owner(OwnerJoinProgress),
191 Joiner(JoinerJoinProgress),
192}
193
194pub trait DeviceJoinRoleProgressKind: Into<DeviceJoinRoleProgress> {
197 const ROLE: DeviceJoinRole;
198}
199
200impl From<OwnerJoinProgress> for DeviceJoinRoleProgress {
201 fn from(progress: OwnerJoinProgress) -> Self {
202 Self::Owner(progress)
203 }
204}
205
206impl DeviceJoinRoleProgressKind for OwnerJoinProgress {
207 const ROLE: DeviceJoinRole = DeviceJoinRole::Owner;
208}
209
210impl From<JoinerJoinProgress> for DeviceJoinRoleProgress {
211 fn from(progress: JoinerJoinProgress) -> Self {
212 Self::Joiner(progress)
213 }
214}
215
216impl DeviceJoinRoleProgressKind for JoinerJoinProgress {
217 const ROLE: DeviceJoinRole = DeviceJoinRole::Joiner;
218}
219
220impl DeviceJoinRoleProgress {
221 pub fn role(&self) -> DeviceJoinRole {
222 match self {
223 Self::Owner(_) => DeviceJoinRole::Owner,
224 Self::Joiner(_) => DeviceJoinRole::Joiner,
225 }
226 }
227
228 pub fn role_name(&self) -> &'static str {
229 self.role().as_str()
230 }
231}
232
233#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
234#[serde(deny_unknown_fields)]
235pub struct DeviceJoinJournalRecord {
236 pub attempt_id: DeviceJoinAttemptId,
237 pub progress: Box<DeviceJoinRoleProgress>,
238}
239
240impl DeviceJoinJournalRecord {
241 pub fn owner_offered(offer: DeviceJoinOffer) -> Self {
242 Self {
243 attempt_id: offer.attempt_id,
244 progress: Box::new(DeviceJoinRoleProgress::Owner(OwnerJoinProgress::Offered(
245 offer,
246 ))),
247 }
248 }
249
250 pub fn store_key(&self) -> String {
251 store_journal_key(self.attempt_id, self.progress.role_name())
252 }
253
254 pub fn store_key_for(attempt_id: DeviceJoinAttemptId, role: DeviceJoinRole) -> String {
255 store_journal_key(attempt_id, role.as_str())
256 }
257
258 pub fn status(&self) -> DeviceJoinStatus {
259 device_join_status(self)
260 }
261
262 pub fn action(&self) -> Option<DeviceJoinAction> {
263 device_join_action(self)
264 }
265
266 pub fn sort_key(&self) -> (DeviceJoinAttemptId, DeviceJoinRole) {
267 (self.attempt_id, self.progress.role())
268 }
269
270 pub fn attempt_key(&self) -> String {
271 attempt_key(self.attempt_id)
272 }
273}
274
275#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
276#[serde(rename_all = "snake_case", deny_unknown_fields)]
277pub enum DeviceJoinRole {
281 Owner,
282 Joiner,
283}
284
285impl DeviceJoinRole {
286 pub fn as_str(self) -> &'static str {
287 match self {
288 Self::Owner => "owner",
289 Self::Joiner => "joiner",
290 }
291 }
292}
293
294pub(crate) fn device_join_status(record: &DeviceJoinJournalRecord) -> DeviceJoinStatus {
295 match &*record.progress {
296 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::Offered(offer))
297 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::OfferReceived(offer)) => {
298 DeviceJoinStatus::AwaitingAccessRequest {
299 offer: offer.clone(),
300 }
301 }
302 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::AccessRequested(request))
303 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::AccessRequested(request)) => {
304 DeviceJoinStatus::AwaitingProviderAdmission {
305 request: request.clone(),
306 }
307 }
308 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::AccessGrantPrepared {
309 request,
310 grant,
311 ..
312 }) => DeviceJoinStatus::ProviderAccessGrantCreatePending {
313 request: request.clone(),
314 grant: grant.clone(),
315 },
316 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ApprovalPrepared(approval))
317 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::ApprovalReceived(approval)) => {
318 DeviceJoinStatus::AwaitingRegistrationRequest {
319 approval: approval.clone(),
320 }
321 }
322 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::RegistrationRequested(request))
323 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::RegistrationPrepared(request)) => {
324 DeviceJoinStatus::AwaitingBootstrap {
325 request: request.clone(),
326 }
327 }
328 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::SamePrincipalActivationCreateIntent {
329 request,
330 ..
331 }) => DeviceJoinStatus::SamePrincipalActivationCreatePending {
332 request: request.clone(),
333 },
334 DeviceJoinRoleProgress::Owner(
335 OwnerJoinProgress::AttemptActivated(bootstrap)
336 | OwnerJoinProgress::ChallengeCreateIntent(bootstrap),
337 ) => DeviceJoinStatus::AwaitingChallengePublication {
338 bootstrap: bootstrap.clone(),
339 },
340 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ProviderReady(bootstrap)) => {
341 DeviceJoinStatus::AwaitingReadiness {
342 bootstrap: bootstrap.clone(),
343 }
344 }
345 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ResponseObserved(readiness))
346 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::Ready(readiness)) => {
347 DeviceJoinStatus::AwaitingProviderCompletion {
348 readiness: readiness.clone(),
349 }
350 }
351 DeviceJoinRoleProgress::Owner(
352 OwnerJoinProgress::ActivationCreateIntent { completion, .. }
353 | OwnerJoinProgress::Completed(completion),
354 ) => DeviceJoinStatus::AwaitingActivation {
355 completion: completion.clone(),
356 },
357 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::SamePrincipalCompleted {
358 join, ..
359 }) => DeviceJoinStatus::SamePrincipalCompleted { join: join.clone() },
360 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ActivationPrepared {
361 activation, ..
362 })
363 | DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::ActivationObserved {
364 activation,
365 ..
366 }) => DeviceJoinStatus::AwaitingCompletion {
367 activation: activation.clone(),
368 },
369 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::Abandoned(abandonment)) => {
370 DeviceJoinStatus::Abandoned {
371 abandonment: abandonment.clone(),
372 }
373 }
374 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::AbandonmentCreateIntent {
375 abandonment,
376 ..
377 }) => DeviceJoinStatus::AbandonmentCreatePending {
378 abandonment: abandonment.clone(),
379 },
380 }
381}
382
383pub fn device_join_action(record: &DeviceJoinJournalRecord) -> Option<DeviceJoinAction> {
384 let resume = || DeviceJoinAction::ResumeOperation {
385 attempt_id: record.attempt_id,
386 role: record.progress.role(),
387 };
388 match &*record.progress {
389 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::Offered(offer)) => {
390 Some(DeviceJoinAction::TransferOffer(offer.clone()))
391 }
392 DeviceJoinRoleProgress::Owner(
393 OwnerJoinProgress::AccessRequested(_)
394 | OwnerJoinProgress::AccessGrantPrepared { .. }
395 | OwnerJoinProgress::RegistrationRequested(_)
396 | OwnerJoinProgress::AttemptActivated(_)
397 | OwnerJoinProgress::ChallengeCreateIntent(_)
398 | OwnerJoinProgress::ResponseObserved(_)
399 | OwnerJoinProgress::Completed(_)
400 | OwnerJoinProgress::SamePrincipalActivationCreateIntent { .. }
401 | OwnerJoinProgress::AbandonmentCreateIntent { .. }
402 | OwnerJoinProgress::ActivationCreateIntent { .. },
403 ) => Some(resume()),
404 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ApprovalPrepared(approval)) => Some(
405 DeviceJoinAction::TransferProviderAdmissionApproval(approval.clone()),
406 ),
407 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ProviderReady(bootstrap)) => Some(
408 DeviceJoinAction::TransferProviderReadyBootstrap(bootstrap.clone()),
409 ),
410 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::SamePrincipalCompleted {
411 join, ..
412 }) => Some(DeviceJoinAction::TransferSamePrincipalJoin(join.clone())),
413 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::ActivationPrepared {
414 activation, ..
415 }) => Some(DeviceJoinAction::TransferActivation(activation.clone())),
416 DeviceJoinRoleProgress::Owner(OwnerJoinProgress::Abandoned(abandonment)) => {
417 Some(DeviceJoinAction::TransferAbandonment(abandonment.clone()))
418 }
419
420 DeviceJoinRoleProgress::Joiner(
421 JoinerJoinProgress::OfferReceived(_) | JoinerJoinProgress::ApprovalReceived(_),
422 ) => Some(resume()),
423 DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::AccessRequested(request)) => Some(
424 DeviceJoinAction::TransferProviderAccessRequest(request.clone()),
425 ),
426 DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::RegistrationPrepared(request)) => Some(
427 DeviceJoinAction::TransferRegistrationRequest(request.clone()),
428 ),
429 DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::Ready(readiness)) => {
430 Some(DeviceJoinAction::TransferReadiness(readiness.clone()))
431 }
432 DeviceJoinRoleProgress::Joiner(JoinerJoinProgress::ActivationObserved {
433 activation,
434 ..
435 }) => Some(DeviceJoinAction::CompleteJoin(activation.clone())),
436 }
437}
438
439pub(crate) fn store_journal_key(attempt_id: DeviceJoinAttemptId, role: &str) -> String {
440 format!("device_join/{}/{role}", attempt_key(attempt_id))
441}
442
443pub fn attempt_key(attempt_id: DeviceJoinAttemptId) -> String {
444 serde_json::to_value(attempt_id)
445 .expect("device join attempt id serialization cannot fail")
446 .as_str()
447 .expect("device join attempt id serializes as a string")
448 .to_string()
449}