coven_database/store/store_session/
device_join_challenges.rs1use crate::*;
2use coven_protocol::provider::{
3 DeviceJoinChallengePublicationProgress, DeviceJoinChallengePublicationRecord,
4};
5
6use super::*;
7
8impl StoreSession<'_> {
9 fn prepare_device_join_challenge_publication(
10 &self,
11 key: &str,
12 value: &str,
13 prepared: &coven_protocol::provider::DeviceJoinChallengePublicationRecord,
14 ) -> Result<coven_protocol::provider::DeviceJoinChallengePublicationRecord, DbError> {
15 let actual = crate::store::store_session::StoreRecords::new(self.conn, self.store_dir)
16 .begin_protocol_state(key, value)?;
17 let actual: coven_protocol::provider::DeviceJoinChallengePublicationRecord =
18 serde_json::from_str(&actual).map_err(|error| {
19 DbError::context("parse device join challenge publication", error)
20 })?;
21 if actual.challenge != prepared.challenge {
22 return Err(DbError::Message(
23 "device join challenge probe id was reused with different bytes".to_string(),
24 ));
25 }
26 Ok(actual)
27 }
28
29 fn publish_device_join_challenge(
30 &self,
31 key: &str,
32 authorization: coven_protocol::provider::DeviceJoinChallengePublicationAuthorization,
33 challenge: coven_protocol::provider::CrossPrincipalProbeChallenge,
34 ) -> Result<(), DbError> {
35 use coven_protocol::provider::{
36 DeviceJoinChallengePublicationProgress, DeviceJoinChallengePublicationRecord,
37 };
38
39 let previous_json =
40 crate::store::store_session::StoreRecords::new(self.conn, self.store_dir)
41 .required_protocol_state(key)?;
42 let previous: DeviceJoinChallengePublicationRecord = serde_json::from_str(&previous_json)
43 .map_err(|error| {
44 DbError::context("parse device join challenge publication", error)
45 })?;
46 if previous.challenge != challenge {
47 return Err(DbError::Message(
48 "device join challenge publication differs from prepared bytes".to_string(),
49 ));
50 }
51 match &previous.progress {
52 DeviceJoinChallengePublicationProgress::Prepared => {
53 let next = DeviceJoinChallengePublicationRecord {
54 challenge,
55 progress: DeviceJoinChallengePublicationProgress::Published { authorization },
56 };
57 let next_json = serde_json::to_string(&next).map_err(|error| {
58 DbError::context("serialize device join challenge publication", error)
59 })?;
60 if !crate::store::store_session::StoreRecords::new(self.conn, self.store_dir)
61 .compare_exchange_protocol_state(key, &previous_json, &next_json)?
62 {
63 return Err(DbError::Message(
64 "device join challenge publication lost its exact predecessor".to_string(),
65 ));
66 }
67 }
68 DeviceJoinChallengePublicationProgress::Published {
69 authorization: existing,
70 } if existing == &authorization => {}
71 DeviceJoinChallengePublicationProgress::Published { .. } => {
72 return Err(DbError::Message(
73 "device join challenge publication authorization changed".to_string(),
74 ));
75 }
76 }
77 Ok(())
78 }
79}
80
81impl StoreDatabase {
82 pub async fn prepare_device_join_challenge_publication(
83 &self,
84 challenge: coven_protocol::provider::CrossPrincipalProbeChallenge,
85 ) -> Result<coven_protocol::provider::DeviceJoinChallengePublicationRecord, DbError> {
86 let key = format!(
87 "device_join_challenge_publication/{}",
88 hex::encode(challenge.probe_id.as_bytes())
89 );
90 let prepared = DeviceJoinChallengePublicationRecord {
91 challenge,
92 progress: DeviceJoinChallengePublicationProgress::Prepared,
93 };
94 let value = serde_json::to_string(&prepared).map_err(|error| {
95 DbError::context("serialize device join challenge publication", error)
96 })?;
97 self.call_store(move |session| {
98 session.prepare_device_join_challenge_publication(&key, &value, &prepared)
99 })
100 .await
101 }
102
103 pub async fn publish_device_join_challenge(
104 &self,
105 authorization: coven_protocol::provider::DeviceJoinChallengePublicationAuthorization,
106 challenge: coven_protocol::provider::CrossPrincipalProbeChallenge,
107 ) -> Result<(), DbError> {
108 let key = format!(
109 "device_join_challenge_publication/{}",
110 hex::encode(challenge.probe_id.as_bytes())
111 );
112 self.call_store(move |session| {
113 session.publish_device_join_challenge(&key, authorization, challenge)
114 })
115 .await
116 }
117}
118
119#[async_trait::async_trait]
120impl coven_protocol::provider::DeviceJoinChallengePublicationJournal for StoreDatabase {
121 async fn prepare(
122 &self,
123 challenge: &coven_protocol::provider::CrossPrincipalProbeChallenge,
124 ) -> Result<
125 coven_protocol::provider::DeviceJoinChallengePublicationRecord,
126 coven_protocol::objects::StorageError,
127 > {
128 self.prepare_device_join_challenge_publication(challenge.clone())
129 .await
130 .map_err(|error| {
131 coven_protocol::objects::StorageError::backend(
132 coven_protocol::objects::StorageBackendFailure::Internal,
133 "prepare device join challenge publication",
134 error,
135 )
136 })
137 }
138
139 async fn claim_published(
140 &self,
141 authorization: &coven_protocol::provider::DeviceJoinChallengePublicationAuthorization,
142 challenge: &coven_protocol::provider::CrossPrincipalProbeChallenge,
143 ) -> Result<(), coven_protocol::objects::StorageError> {
144 self.publish_device_join_challenge(authorization.clone(), challenge.clone())
145 .await
146 .map_err(|error| {
147 coven_protocol::objects::StorageError::backend(
148 coven_protocol::objects::StorageBackendFailure::Internal,
149 "publish device join challenge",
150 error,
151 )
152 })
153 }
154}